feature(Slutredovisning): Formulär för att skapa och skicka in slutredovisning (TV-533)
Squashed commit of the following: commit 5b427cdd62b881cc32d408beaf92f8e3c22a9bd4 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Fri Nov 12 09:24:18 2021 +0100 refactor commit b51af1b25b573a3203f3dd7e8e1c3e1401f0d228 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Fri Nov 12 09:20:58 2021 +0100 add yrke names commit 1ed3a33fb997669bf1af0be52347b371af0177ba Merge: 4d9d25220235f1f9Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Fri Nov 12 07:40:48 2021 +0100 Merge branch 'develop' into feature/tv-533-Skapa-slutredovisning-2 # Conflicts: # apps/mina-sidor-fa/src/app/shared/enums/feature.enum.ts commit 4d9d25226fc3b91386832b1367edcf016d336c03 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Nov 11 16:34:24 2021 +0100 Fixed switch functionality inside slutredovisning model commit cce93c169e39bba681e4e25f511fa42686134f97 Merge: b56ce52f 51ed2e84 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Thu Nov 11 16:15:46 2021 +0100 Fixed conflicts and added deltid percent commit 51ed2e841e00e8d7cea9225bd9cfeab8b783efbd Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 14:49:40 2021 +0100 hide description on step 1 and 2 commit 269708be2f7fec84815b3e7f91e33d4c305b0846 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 14:04:59 2021 +0100 cleanup commit 05df3acf97f6115ec15cd295c1ed12721592f867 Merge: 3c803f38c6adc71fAuthor: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 13:47:22 2021 +0100 Merge branch 'develop' into feature/tv-533-Skapa-slutredovisning-2 commit 3c803f3883b23065c2e0c15f6946213e5972576b Merge: c2724aa6f332dd41Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 12:38:53 2021 +0100 Merge branch 'develop' into feature/tv-533-Skapa-slutredovisning-2 # Conflicts: # apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-details/pages/deltagare-card/components/deltagare-tab-reports/components/reports-list/reports-list.component.ts # apps/mina-sidor-fa/src/app/shared/enums/report-type.enum.ts commit c2724aa654483001d48ca1c2d0a5bfc1b2552c89 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 12:22:05 2021 +0100 add loader commit b97b45fb22260dbdc9f9792e027b7bda68150ead Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 12:09:09 2021 +0100 move formgroups to parent to persist stat commit 37feaab19309777ef96fb70be62ede9e26498b89 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 10:43:53 2021 +0100 fix radiobuttons commit 644b9fd6288589cc3a66ceb8beefb623345e4308 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 09:30:31 2021 +0100 . commit 28875f21a381a966054523ff7ccb7df126b30120 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 08:20:07 2021 +0100 move submit logic to step3 commit eef52c39dd970abbedc413b4c348098e07abe036 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Thu Nov 11 07:40:56 2021 +0100 submit button commit 4d732240e0ff6196d827572ff865392d6aaa0a9c Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Nov 10 12:47:43 2021 +0100 fix some bugs commit 642cc2b24fd3a5def8abb9ae45595d101ccdcfb7 Merge: c4d534072dc56685Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Nov 10 12:24:18 2021 +0100 Merge branch 'develop' into feature/tv-533-Skapa-slutredovisning-2 commit c4d53407399d695f58e5a0be074d81bbc1b1cb21 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Nov 10 12:24:09 2021 +0100 wip commit 808810699850e611ead2ee7ed1b728acc3d93dd5 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Nov 10 12:02:01 2021 +0100 slutredovisning commit fa446717acc66e779ecdb87ba7a5b2224eeca9fe Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Nov 10 11:00:44 2021 +0100 view for utbildning done ... and 64 more commits
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<digi-typography>
|
||||
<h1>{{ reportTitle }}</h1>
|
||||
<p class="report-layout__description" *ngIf="description">{{description}}</p>
|
||||
<msfa-report-description-list [avrop]="avrop"></msfa-report-description-list>
|
||||
<msfa-report-description-list *ngIf="showAvropDetails" [avrop]="avrop"></msfa-report-description-list>
|
||||
<div class="report-layout__main-content">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
@@ -14,4 +14,5 @@ export class ReportLayoutComponent {
|
||||
@Input() startDate: string;
|
||||
@Input() endDate: string;
|
||||
@Input() avrop: Avrop;
|
||||
@Input() showAvropDetails = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<h3>Huvudsysselsättning</h3>
|
||||
<dl>
|
||||
<dt>Vilken är deltagarens huvudsakliga sysselsättning just nu?</dt>
|
||||
<dd>{{mainOccupationName}}</dd>
|
||||
|
||||
<ng-container *ngIf="education">
|
||||
<dt>Utbildningsnivå</dt>
|
||||
<dd>{{educationLevel }}</dd>
|
||||
<ng-container *ngIf="education.otherExplanation">
|
||||
<dt>Beskrivning av Annat</dt>
|
||||
<dd>{{education.otherExplanation }}</dd>
|
||||
</ng-container>
|
||||
|
||||
<dt>Utbildningens längd</dt>
|
||||
<dd>{{educationLength }}</dd>
|
||||
<dt>Inriktning på utbildningen</dt>
|
||||
<dd>{{education.educationSpecification }}</dd>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="work">
|
||||
<ng-container *ngFor="let workItem of work">
|
||||
<dt>Yrkesområde</dt>
|
||||
<dd>{{workItem.yrkesomradeName }}</dd>
|
||||
<dt>Yrkesgrupp</dt>
|
||||
<dd>{{workItem.yrkesgruppName }}</dd>
|
||||
<dt>Anställningsform</dt>
|
||||
<dd>{{workItem.anstallningsform }}</dd>
|
||||
<ng-container *ngIf="workItem.otherExplanation">
|
||||
<dt>Beskrivning av Annat</dt>
|
||||
<dd>{{workItem.otherExplanation }}</dd>
|
||||
</ng-container>
|
||||
|
||||
<dt>Omfattning</dt>
|
||||
<dd>{{omfattningToString(workItem.omfattning) }}</dd>
|
||||
<dt>Omfattning i procent</dt>
|
||||
<dd>{{workItem.omfattningPercent }}</dd>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="stillUnemployed">
|
||||
<dt>Anledning till att arbetssökanden under tjänstens gång inte nått målet:</dt>
|
||||
|
||||
<ul>
|
||||
<ng-container *ngFor="let reason of stillUnemployed.reasonsGoalNotReached">
|
||||
<li>{{capitalizeSentence(reason) }}</li>
|
||||
</ng-container>
|
||||
</ul>
|
||||
|
||||
<ng-container *ngIf="stillUnemployed.otherExplanation">
|
||||
<dt>Beskrivning av Annat</dt>
|
||||
<dd>{{stillUnemployed.otherExplanation }}</dd>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="other">
|
||||
<dt>Förtydling av Annan huvudsysselsättning:</dt>
|
||||
<dd>{{other.otherExplanation }}</dd>
|
||||
</ng-container>
|
||||
</dl>
|
||||
|
||||
<h3>Aktiviteter</h3>
|
||||
<dl>
|
||||
<ng-container *ngFor="let activity of slutredovisning.activities">
|
||||
<dt>{{activity.name}}</dt>
|
||||
<dd>
|
||||
<pre>{{activity.whatHasBeenDone}}</pre>
|
||||
</dd>
|
||||
</ng-container>
|
||||
</dl>
|
||||
|
||||
<h3>Deltagarens framsteg och utveckling</h3>
|
||||
<dl>
|
||||
<dt>Beskriv deltagarens framsteg och utveckling under perioden</dt>
|
||||
<dd><pre>{{slutredovisning.progressDescription}}</pre></dd>
|
||||
<dt>Information om lämpligt nästa steg för deltagaren</dt>
|
||||
<dd><pre>{{slutredovisning.nextStepDescription}}</pre></dd>
|
||||
<dt>Övrig information</dt>
|
||||
<dd><pre>{{slutredovisning.otherInformation}}</pre></dd>
|
||||
</dl>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningViewDescriptionListComponent } from './slutredovisning-view-description-list.component';
|
||||
|
||||
describe('SlutredovisningViewDescriptionListComponent', () => {
|
||||
let component: SlutredovisningViewDescriptionListComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningViewDescriptionListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SlutredovisningViewDescriptionListComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningViewDescriptionListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import {
|
||||
educationLengthToString,
|
||||
educationLevelToString,
|
||||
MainOccupation,
|
||||
mainOccupationToString,
|
||||
Omfattning,
|
||||
omfattningToString,
|
||||
Slutredovisning,
|
||||
} from '@msfa-models/slutredovisning.model';
|
||||
import { capitalizeSentence } from '@msfa-utils/capitalize-sentence.util';
|
||||
import {
|
||||
SlutredovisningResponseMainOccupationEducationDetails,
|
||||
SlutredovisningResponseMainOccupationOtherDetails,
|
||||
SlutredovisningResponseMainOccupationStillUnemployedDetails,
|
||||
SlutredovisningResponseMainOccupationWorkDetails,
|
||||
} from '@msfa-models/api/slutredovisning.response.model';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-view-description-list',
|
||||
templateUrl: './slutredovisning-view-description-list.component.html',
|
||||
styleUrls: ['./slutredovisning-view-description-list.component.css'],
|
||||
})
|
||||
export class SlutredovisningViewDescriptionListComponent {
|
||||
capitalizeSentence = capitalizeSentence;
|
||||
@Input() slutredovisning: Slutredovisning;
|
||||
|
||||
get mainOccupationName(): string | null {
|
||||
return mainOccupationToString(this.slutredovisning?.mainOccupation?.type);
|
||||
}
|
||||
|
||||
get education(): SlutredovisningResponseMainOccupationEducationDetails {
|
||||
return this.slutredovisning?.mainOccupation?.type === MainOccupation.Education
|
||||
? this.slutredovisning.mainOccupation.education
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get work(): SlutredovisningResponseMainOccupationWorkDetails[] {
|
||||
return this.slutredovisning?.mainOccupation?.type === MainOccupation.Work
|
||||
? this.slutredovisning.mainOccupation.work
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get stillUnemployed(): SlutredovisningResponseMainOccupationStillUnemployedDetails {
|
||||
return this.slutredovisning?.mainOccupation?.type === MainOccupation.StillUnemployed
|
||||
? this.slutredovisning.mainOccupation.stillUnemployed
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get other(): SlutredovisningResponseMainOccupationOtherDetails {
|
||||
return this.slutredovisning?.mainOccupation?.type === MainOccupation.Other
|
||||
? this.slutredovisning.mainOccupation.other
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get educationLevel(): string {
|
||||
return educationLevelToString(this.education.educationLevel);
|
||||
}
|
||||
get educationLength(): string {
|
||||
return educationLengthToString(this.education.educationLength);
|
||||
}
|
||||
omfattningToString(omfattning: Omfattning): string {
|
||||
return omfattningToString(omfattning);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { SlutredovisningViewService } from '../../pages/report-views/slutredovisning-view/slutredovisning-view.service';
|
||||
import { UiLoaderModule } from '@ui/loader/loader.module';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { UiSkeletonModule } from '@ui/skeleton/skeleton.module';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { SlutredovisningViewDescriptionListComponent } from './slutredovisning-view-description-list.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [SlutredovisningViewDescriptionListComponent],
|
||||
imports: [CommonModule, UiLoaderModule, UiSkeletonModule],
|
||||
providers: [SlutredovisningViewService],
|
||||
exports: [SlutredovisningViewDescriptionListComponent],
|
||||
})
|
||||
export class SlutredovisningViewDescriptionListModule {}
|
||||
@@ -110,6 +110,26 @@ activeFeatures.forEach(feature => {
|
||||
}
|
||||
);
|
||||
break;
|
||||
case Feature.SLUTREDOVISNING:
|
||||
routes.push(
|
||||
{
|
||||
path: 'slutredovisning',
|
||||
data: { title: 'Skapa slutredovisning' },
|
||||
loadChildren: () =>
|
||||
import('./pages/report-forms/slutredovisning-form/slutredovisning-form.module').then(
|
||||
m => m.SlutredovisningFormModule
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'slutredovisning/:handlingId',
|
||||
data: { title: 'Slutredovisning' },
|
||||
loadChildren: () =>
|
||||
import('./pages/report-views/slutredovisning-view/slutredovisning-view.module').then(
|
||||
m => m.SlutredovisningViewModule
|
||||
),
|
||||
}
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ export class ReportsListComponent {
|
||||
return `./avvikelserapport/${report.id}`;
|
||||
case ReportType.PeriodiskRedovisning:
|
||||
return `./periodisk-redovisning/${report.id}`;
|
||||
case ReportType.Slutredovisning:
|
||||
return `./slutredovisning/${report.id}`;
|
||||
case ReportType.InformativRapport:
|
||||
case ReportType.InformativRedovisning:
|
||||
return `./informativ-rapport/${report.id}`;
|
||||
|
||||
@@ -45,6 +45,13 @@
|
||||
afText="Informativ rapport"
|
||||
></digi-ng-link-button>
|
||||
</li>
|
||||
<li *ngIf="slutredovisningButtonVisible">
|
||||
<digi-ng-link-button
|
||||
class="deltagare-tab-reports__button"
|
||||
afRoute="./slutredovisning"
|
||||
afText="Slutredovisning"
|
||||
></digi-ng-link-button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ng-container *ngIf="reportsData$ | async as reportsData; else loadingRef">
|
||||
|
||||
@@ -36,6 +36,9 @@ export class DeltagareTabReportsComponent {
|
||||
get informativRapportButtonVisible(): boolean {
|
||||
return this._activeFeatures.includes(Feature.REPORTING_INFORMATIV_RAPPORT);
|
||||
}
|
||||
get slutredovisningButtonVisible(): boolean {
|
||||
return this._activeFeatures.includes(Feature.SLUTREDOVISNING);
|
||||
}
|
||||
|
||||
constructor(private activatedRoute: ActivatedRoute, private deltagareApiService: DeltagareApiService) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
Anstallningsform,
|
||||
EducationLength,
|
||||
EducationLevel,
|
||||
MainOccupation,
|
||||
Omfattning,
|
||||
StillUnemployedReason,
|
||||
} from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationWorkDetails {
|
||||
yrkesomrade: string;
|
||||
yrkesgrupp: string;
|
||||
anstallningsform: Anstallningsform;
|
||||
otherExplanation: string;
|
||||
omfattning: Omfattning;
|
||||
omfattningPercent: number;
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationWork {
|
||||
type: MainOccupation.Work;
|
||||
work: SlutredovisningFormDataMainOccupationWorkDetails[];
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationEducationDetails {
|
||||
educationLevel: EducationLevel;
|
||||
otherExplanation: string;
|
||||
educationLength: EducationLength;
|
||||
educationSpecification: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationEducation {
|
||||
type: MainOccupation.Education;
|
||||
education: SlutredovisningFormDataMainOccupationEducationDetails;
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationOtherDetails {
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationOther {
|
||||
type: MainOccupation.Other;
|
||||
other: SlutredovisningFormDataMainOccupationOtherDetails;
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationStillUnemployedDetails {
|
||||
reasonsGoalNotReached: StillUnemployedReason[];
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningFormDataMainOccupationStillUnemployed {
|
||||
type: MainOccupation.StillUnemployed;
|
||||
stillUnemployed: SlutredovisningFormDataMainOccupationStillUnemployedDetails;
|
||||
}
|
||||
|
||||
export type SlutredovisningFormDataMainOccupationDetails =
|
||||
| SlutredovisningFormDataMainOccupationWork
|
||||
| SlutredovisningFormDataMainOccupationEducation
|
||||
| SlutredovisningFormDataMainOccupationOther
|
||||
| SlutredovisningFormDataMainOccupationStillUnemployed;
|
||||
|
||||
export interface SlutredovisningFormData {
|
||||
genomforandereferens: number;
|
||||
mainOccupation: SlutredovisningFormDataMainOccupationDetails;
|
||||
activities: { id: string; whatHasBeenDone: string; name: string }[];
|
||||
progressDescription: string;
|
||||
nextStepDescription: string;
|
||||
otherInformation: string;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<div class="slutredovisning-form-step0-education">
|
||||
<div class="slutredovisning-form-step0-education__level">
|
||||
<digi-form-fieldset af-legend="Utbildningsnivå" af-name="mainOccupationx" af-form="report-form">
|
||||
<ui-radiobutton-group
|
||||
[uiRadiobuttons]="educationLevelOptions"
|
||||
[formControl]="educationLevelFormControl"
|
||||
uiName="educationLevel"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid(educationLevelFormControl)"
|
||||
[uiValidationMessage]="educationLevelFormControl.errors?.required"
|
||||
></ui-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<ui-textarea
|
||||
*ngIf="educationLevelFormControl.value === EducationLevel.Annat"
|
||||
[formControl]="otherExplanationFormControl"
|
||||
uiLabel="Beskrivning av Annat"
|
||||
[uiValidationMessage]="formGroup?.errors?.needDescriptionOfOther"
|
||||
[uiAnnounceIfOptional]="false"
|
||||
[uiMaxLength]="200"
|
||||
[uiInvalid]="(otherExplanationFormControl?.touched || this.shouldValidate) && formGroup.errors?.needDescriptionOfOther"
|
||||
></ui-textarea>
|
||||
</div>
|
||||
|
||||
<digi-form-fieldset af-legend="Utbildningens längd" af-name="mainOccupationx" af-form="report-form">
|
||||
<ui-radiobutton-group
|
||||
[uiRadiobuttons]="educationLengthOptions"
|
||||
[formControl]="educationLengthFormControl"
|
||||
uiName="educationLength"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid(educationLengthFormControl)"
|
||||
[uiValidationMessage]="educationLengthFormControl.errors?.required"
|
||||
></ui-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<ui-textarea
|
||||
[formControl]="educationSpecificationFormControl"
|
||||
uiLabel="Inriktning på utbildningen"
|
||||
[uiValidationMessage]="educationSpecificationFormControl?.errors?.required"
|
||||
[uiAnnounceIfOptional]="false"
|
||||
[uiMaxLength]="200"
|
||||
[uiInvalid]="(educationSpecificationFormControl?.touched || this.shouldValidate) && educationSpecificationFormControl.errors?.required"
|
||||
></ui-textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
@import 'mixins/list';
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-form-step0-education {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
|
||||
&__level {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep0EducationComponent } from './slutredovisning-form-step0-education.component';
|
||||
|
||||
describe('SlutredovisningFormStep1Component', () => {
|
||||
let component: SlutredovisningFormStep0EducationComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep0EducationComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep0EducationComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep0EducationComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
import { SlutredovisningFormStep0Education } from './slutredovisning-form-step0-education.validator';
|
||||
import { EducationLength, EducationLevel, MainOccupation } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningFormStep0EducationFormData {
|
||||
educationLevel: EducationLevel;
|
||||
educationLength: EducationLength;
|
||||
otherExplanation?: string;
|
||||
educationSpecification: string;
|
||||
}
|
||||
|
||||
type FormKeys = keyof SlutredovisningFormStep0EducationFormData;
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step0-education',
|
||||
templateUrl: './slutredovisning-form-step0-education.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step0-education.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep0EducationComponent implements OnInit {
|
||||
readonly formGroupKey: MainOccupation = MainOccupation.Education;
|
||||
@Input() formGroupRef: FormGroup;
|
||||
|
||||
@Input() formData: SlutredovisningFormStep0EducationFormData;
|
||||
EducationLevel = EducationLevel;
|
||||
@Input() shouldValidate: boolean;
|
||||
@Output() nextClick = new EventEmitter<SlutredovisningFormStep0EducationFormData>();
|
||||
@Output() backClick = new EventEmitter<void>();
|
||||
|
||||
private educationLevelFormControlName: FormKeys = 'educationLevel';
|
||||
private educationLengthFormControlName: FormKeys = 'educationLength';
|
||||
private otherExplanationFormControlName: FormKeys = 'otherExplanation';
|
||||
private educationSpecificationFormControlName: FormKeys = 'educationSpecification';
|
||||
|
||||
// utbildningsniva
|
||||
educationLevelOptions: RadiobuttonModel[] = [
|
||||
{ label: 'Högskola eller universitet', value: EducationLevel.HogskolaEllerUniversitet },
|
||||
{ label: 'Yrkeshögskola', value: EducationLevel.Yrkeshogskola },
|
||||
{ label: 'Komvux, gymnasium eller folkhögskola', value: EducationLevel.KomvuxGymnasiumFolkhogskola },
|
||||
{
|
||||
label: 'Vet ej',
|
||||
value: EducationLevel.VetEj,
|
||||
},
|
||||
{
|
||||
label: 'Annat',
|
||||
value: EducationLevel.Annat,
|
||||
},
|
||||
];
|
||||
|
||||
// langd_utbildning
|
||||
educationLengthOptions: RadiobuttonModel[] = [
|
||||
{ label: 'Upp till ett år', value: EducationLength.UpToOneYear },
|
||||
{ label: 'Ett år till två år', value: EducationLength.OneToTwoYears },
|
||||
{ label: 'Två år eller längre', value: EducationLength.OverTwoYears },
|
||||
];
|
||||
|
||||
formGroup: FormGroup | AbstractControl;
|
||||
|
||||
get educationLevelFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.educationLevelFormControlName);
|
||||
}
|
||||
|
||||
get educationLengthFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.educationLengthFormControlName);
|
||||
}
|
||||
|
||||
get otherExplanationFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.otherExplanationFormControlName);
|
||||
}
|
||||
get educationSpecificationFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.educationSpecificationFormControlName);
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.formGroupRef.get(this.formGroupKey)) {
|
||||
this.formGroup = this.formGroupRef.get(this.formGroupKey);
|
||||
} else {
|
||||
this.formGroup = new FormGroup(
|
||||
{
|
||||
[this.educationLevelFormControlName]: new FormControl(null, [
|
||||
RequiredValidator('Utbildningsnivå är obligatoriskt'),
|
||||
]),
|
||||
[this.otherExplanationFormControlName]: new FormControl(null),
|
||||
[this.educationLengthFormControlName]: new FormControl(null, [
|
||||
RequiredValidator('Utbildningens längd är obligatorisk'),
|
||||
]),
|
||||
[this.educationSpecificationFormControlName]: new FormControl(null, [
|
||||
RequiredValidator('Utbildningens inriktning är obligatorisk'),
|
||||
]),
|
||||
},
|
||||
[SlutredovisningFormStep0Education.slutredovisningFormStep1EducationIsValid()]
|
||||
);
|
||||
this.formGroupRef.addControl(this.formGroupKey, this.formGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { SlutredovisningFormStep0EducationFormData } from './slutredovisning-form-step0-education.component';
|
||||
import { EducationLevel } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningFormErrors {
|
||||
needDescriptionOfOther?: string;
|
||||
}
|
||||
|
||||
export class SlutredovisningFormStep0Education {
|
||||
static slutredovisningFormStep1EducationIsValid(): ValidatorFn {
|
||||
return (c: AbstractControl): SlutredovisningFormErrors => {
|
||||
let errors: SlutredovisningFormErrors = {};
|
||||
|
||||
const { educationLevel, otherExplanation } = c.value as SlutredovisningFormStep0EducationFormData;
|
||||
|
||||
if (educationLevel === EducationLevel.Annat && (!otherExplanation || otherExplanation?.length <= 0)) {
|
||||
errors = {
|
||||
...errors,
|
||||
needDescriptionOfOther: 'Ifall Annat är valt som utbildningsnivå krävs en beskrivning.',
|
||||
};
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<div class="slutredovisning-report-form-step0-still-unemployed">
|
||||
<div class="slutredovisning-report-form-step0-still-unemployed__form">
|
||||
<ui-textarea
|
||||
[formControl]="otherExplanationFormControl"
|
||||
uiLabel="Beskrivning av Annat"
|
||||
[uiValidationMessage]="otherExplanationFormControl?.errors?.required"
|
||||
[uiAnnounceIfOptional]="false"
|
||||
[uiMaxLength]="200"
|
||||
[uiRequired]="true"
|
||||
[uiInvalid]="(otherExplanationFormControl?.touched || this.shouldValidate) && otherExplanationFormControl.errors?.required"
|
||||
></ui-textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
@import 'mixins/list';
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-report-form-step0-still-unemployed {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
|
||||
&__confirmation,
|
||||
&__warning,
|
||||
&__form {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__cta-wrapper {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep0OtherComponent } from './slutredovisning-form-step0-other.component';
|
||||
|
||||
describe('SlutredovisningFormStep1Component', () => {
|
||||
let component: SlutredovisningFormStep0OtherComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep0OtherComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep0OtherComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep0OtherComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
import { MainOccupation } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningFormStep0OtherFormData {
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
type FormKeys = keyof SlutredovisningFormStep0OtherFormData;
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step0-other',
|
||||
templateUrl: './slutredovisning-form-step0-other.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step0-other.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep0OtherComponent implements OnInit {
|
||||
readonly formGroupKey: MainOccupation = MainOccupation.Other;
|
||||
@Input() formGroupRef: FormGroup;
|
||||
|
||||
@Input() formData: SlutredovisningFormStep0OtherFormData;
|
||||
@Input() shouldValidate: boolean;
|
||||
@Output() nextClick = new EventEmitter<SlutredovisningFormStep0OtherFormData>();
|
||||
@Output() backClick = new EventEmitter<void>();
|
||||
|
||||
private otherExplanationFormControlName: FormKeys = 'otherExplanation';
|
||||
|
||||
formGroup: FormGroup | AbstractControl;
|
||||
|
||||
get otherExplanationFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.otherExplanationFormControlName);
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.formGroupRef.get(this.formGroupKey)) {
|
||||
this.formGroup = this.formGroupRef.get(this.formGroupKey);
|
||||
} else {
|
||||
this.formGroup = new FormGroup({
|
||||
[this.otherExplanationFormControlName]: new FormControl(
|
||||
null,
|
||||
RequiredValidator('En beskrivning av Annat är obligatoriskt.')
|
||||
),
|
||||
});
|
||||
this.formGroupRef.addControl(this.formGroupKey, this.formGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<div class="slutredovisning-report-form-step0-still-unemployed">
|
||||
<div class="slutredovisning-report-form-step0-still-unemployed__form">
|
||||
<div>
|
||||
<digi-form-fieldset
|
||||
af-legend="Anledning till att arbetssökanden under tjänstens gång inte har nått målet"
|
||||
af-name="stillUnemployed"
|
||||
af-form="report-form"
|
||||
[formGroup]="stillUnemployedReasonFormControl"
|
||||
>
|
||||
<ui-checkbox
|
||||
*ngFor="let stillUnemployedReasonOption of stillUnemployedReasonOptions"
|
||||
[formControl]="stillUnemployedReasonFormControl.get(stillUnemployedReasonOption.formControlName)"
|
||||
[uiLabel]="stillUnemployedReasonOption.label"
|
||||
[uiInvalid]="(stillUnemployedReasonFormControl?.touched || this.shouldValidate) && formGroup?.errors?.atLeastOneSelected"
|
||||
></ui-checkbox>
|
||||
</digi-form-fieldset>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-form-validation-message
|
||||
*ngIf="(stillUnemployedReasonFormControl?.touched || this.shouldValidate) && formGroup?.errors?.atLeastOneSelected"
|
||||
af-variation="error"
|
||||
>{{formGroup?.errors?.atLeastOneSelected}}</digi-form-validation-message
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ui-textarea
|
||||
*ngIf="showExplanationTextArea"
|
||||
[formControl]="stillUnemployedReasonDescriptionFormControl"
|
||||
uiLabel="Beskrivning av Annat"
|
||||
[uiValidationMessage]="formGroup?.errors?.needDescriptionOfOther"
|
||||
[uiAnnounceIfOptional]="false"
|
||||
[uiMaxLength]="200"
|
||||
[uiInvalid]="(stillUnemployedReasonDescriptionFormControl?.touched || this.shouldValidate) && formGroup.errors?.needDescriptionOfOther"
|
||||
></ui-textarea>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,26 @@
|
||||
@import 'mixins/list';
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-report-form-step0-still-unemployed {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
|
||||
&__confirmation,
|
||||
&__warning,
|
||||
&__form {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
&__cta-wrapper {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep0StillUnemployedComponent } from './slutredovisning-form-step0-still-unemployed.component';
|
||||
|
||||
describe('SlutredovisningFormStep1Component', () => {
|
||||
let component: SlutredovisningFormStep0StillUnemployedComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep0StillUnemployedComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep0StillUnemployedComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep0StillUnemployedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { SlutredovisningFormStep0StillUnemployed } from './slutredovisning-form-step0-still-unemployed.validator';
|
||||
import { MainOccupation, StillUnemployedReason } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
type ReasonCheckboxValues = { [key: string]: boolean };
|
||||
|
||||
export interface SlutredovisningFormStep0StillUnemployedFormData {
|
||||
reasonsGoalNotReached: ReasonCheckboxValues;
|
||||
stillUnemployedExplanation?: string;
|
||||
}
|
||||
|
||||
type FormKeys = keyof SlutredovisningFormStep0StillUnemployedFormData;
|
||||
|
||||
interface CheckboxModel {
|
||||
label: string;
|
||||
formControlName: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step0-still-unemployed',
|
||||
templateUrl: './slutredovisning-form-step0-still-unemployed.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step0-still-unemployed.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep0StillUnemployedComponent implements OnInit {
|
||||
readonly formGroupKey: MainOccupation = MainOccupation.StillUnemployed;
|
||||
@Input() formGroupRef: FormGroup;
|
||||
|
||||
@Input() formData: SlutredovisningFormStep0StillUnemployedFormData;
|
||||
StillUnemployedReason = StillUnemployedReason;
|
||||
@Input() shouldValidate: boolean;
|
||||
@Output() nextClick = new EventEmitter<SlutredovisningFormStep0StillUnemployedFormData>();
|
||||
@Output() backClick = new EventEmitter<void>();
|
||||
|
||||
private stillUnemployedReasonFormControlName: FormKeys = 'reasonsGoalNotReached';
|
||||
private stillUnemployedExplanationFormControlName: FormKeys = 'stillUnemployedExplanation';
|
||||
|
||||
//anledning_mal_ej_uppnatt
|
||||
stillUnemployedReasonOptions: CheckboxModel[] = [
|
||||
{ label: 'Saknar relevant utbildning', formControlName: StillUnemployedReason.SaknarRelevantUtbildning },
|
||||
{ label: 'Saknar arbetslivserfarenhet', formControlName: StillUnemployedReason.SaknarArbetslivserfarenhet },
|
||||
{ label: 'Saknar nätverk och kontakter', formControlName: StillUnemployedReason.SaknarNatverkOchKontakter },
|
||||
{
|
||||
label: 'Bristande språkkunskaper i svenska',
|
||||
formControlName: StillUnemployedReason.BristandeSprakkunskaperISvenska,
|
||||
},
|
||||
{
|
||||
label: 'Konjunkturläget',
|
||||
formControlName: StillUnemployedReason.KonjukturLaget,
|
||||
},
|
||||
{
|
||||
label: 'Annat',
|
||||
formControlName: StillUnemployedReason.Annat,
|
||||
},
|
||||
];
|
||||
|
||||
formGroup: FormGroup | AbstractControl;
|
||||
|
||||
get showExplanationTextArea(): boolean {
|
||||
const reasonsValue: ReasonCheckboxValues | undefined = this.stillUnemployedReasonFormControl
|
||||
?.value as ReasonCheckboxValues;
|
||||
return reasonsValue[StillUnemployedReason.Annat] === true;
|
||||
}
|
||||
|
||||
get stillUnemployedReasonFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.stillUnemployedReasonFormControlName);
|
||||
}
|
||||
|
||||
get stillUnemployedReasonDescriptionFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.stillUnemployedExplanationFormControlName);
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.formGroupRef.get(this.formGroupKey)) {
|
||||
this.formGroup = this.formGroupRef.get(this.formGroupKey);
|
||||
} else {
|
||||
const reasonsFormControls: { [key: string]: AbstractControl } = {};
|
||||
|
||||
this.stillUnemployedReasonOptions.forEach(reason => {
|
||||
reasonsFormControls[reason.formControlName] = new FormControl(false);
|
||||
});
|
||||
|
||||
this.formGroup = new FormGroup(
|
||||
{
|
||||
[this.stillUnemployedReasonFormControlName]: new FormGroup(reasonsFormControls),
|
||||
[this.stillUnemployedExplanationFormControlName]: new FormControl(null),
|
||||
},
|
||||
[SlutredovisningFormStep0StillUnemployed.slutredovisningFormStep0StillUnemployedIsValid()]
|
||||
);
|
||||
this.formGroupRef.addControl(this.formGroupKey, this.formGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { SlutredovisningFormStep0StillUnemployedFormData } from './slutredovisning-form-step0-still-unemployed.component';
|
||||
import { StillUnemployedReason } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningStillUnemployedFormErrors {
|
||||
needDescriptionOfOther?: string;
|
||||
atLeastOneSelected?: string;
|
||||
}
|
||||
|
||||
export class SlutredovisningFormStep0StillUnemployed {
|
||||
static slutredovisningFormStep0StillUnemployedIsValid(): ValidatorFn {
|
||||
return (c: AbstractControl): SlutredovisningStillUnemployedFormErrors => {
|
||||
let errors: SlutredovisningStillUnemployedFormErrors = {};
|
||||
|
||||
const {
|
||||
reasonsGoalNotReached,
|
||||
stillUnemployedExplanation,
|
||||
} = c.value as SlutredovisningFormStep0StillUnemployedFormData;
|
||||
|
||||
if (Object.values(reasonsGoalNotReached).every(reasonIsChecked => !reasonIsChecked)) {
|
||||
errors = {
|
||||
...errors,
|
||||
atLeastOneSelected: 'Minst en anledning måste väljas.',
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: Is it required that at least one reason is chosen? Check with Bea and Kia
|
||||
if (
|
||||
reasonsGoalNotReached[StillUnemployedReason.Annat] === true &&
|
||||
(!stillUnemployedExplanation || stillUnemployedExplanation?.length <= 0)
|
||||
) {
|
||||
errors = {
|
||||
...errors,
|
||||
needDescriptionOfOther: 'Ifall Annat är valt som utbildningsnivå krävs en beskrivning.',
|
||||
};
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface SlutredovisningFormStep0WorkErrors {
|
||||
yrkesomrade?: string;
|
||||
yrkesgrupp?: string;
|
||||
anstallningsform?: string;
|
||||
annatAnstallningComment?: string;
|
||||
omfattning?: string;
|
||||
omfattningPercent?: string;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<div class="slutredovisning-form-step0-work" [formGroup]="formGroupRef">
|
||||
<ng-container [formArrayName]="MainOccupation.Work">
|
||||
<!-- <ui-error-list uiHeadingText="Something is wrong" [uiErrorLinks]="errorListErrors"></ui-error-list> -->
|
||||
<h2 class="slutredovisning-form-step0-work__heading">Inom vilket/vilka yrken</h2>
|
||||
<div
|
||||
class="slutredovisning-form-step0-work__group"
|
||||
*ngFor="let formGroup of formArray.controls; let index = index"
|
||||
[formGroupName]="index"
|
||||
>
|
||||
<div class="slutredovisning-form-step0-work__group-header">
|
||||
<h3 class="slutredovisning-form-step0-work__heading">Yrke {{index + 1}}</h3>
|
||||
<digi-button
|
||||
*ngIf="formArray.controls.length < 2"
|
||||
af-size="s"
|
||||
af-variation="tertiary"
|
||||
(afOnClick)="addNewFormGroupToFormArray()"
|
||||
>
|
||||
<msfa-icon icon="plus" size="s" slot="icon"></msfa-icon>
|
||||
Lägg till yrke
|
||||
</digi-button>
|
||||
<digi-button
|
||||
*ngIf="formArray.controls.length === 2"
|
||||
af-size="s"
|
||||
af-variation="tertiary"
|
||||
(afOnClick)="removeGroup(index)"
|
||||
>
|
||||
<msfa-icon icon="x" size="s" slot="icon"></msfa-icon>
|
||||
Ta bort yrket
|
||||
</digi-button>
|
||||
</div>
|
||||
<ui-select
|
||||
*ngIf="yrkesomradeSelectOptions$ | async as yrkesomradeSelectOptions"
|
||||
formControlName="yrkesomrade"
|
||||
uiLabel="Yrkesområde"
|
||||
uiPlaceholder="Välj yrkesområde"
|
||||
[uiId]="'slutredovisning-yrkesomrade-control-'+index"
|
||||
[uiOptions]="yrkesomradeSelectOptions"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid('yrkesomrade', index)"
|
||||
[uiValidationMessage]="formControlError('yrkesomrade', index)"
|
||||
></ui-select>
|
||||
<ui-select
|
||||
*ngIf="getYrkesgruppSelectOptions$(index) | async as yrkesgruppSelectOptions"
|
||||
formControlName="yrkesgrupp"
|
||||
uiLabel="Yrkesgrupp"
|
||||
uiPlaceholder="Välj yrkesgrupp"
|
||||
[uiId]="'slutredovisning-yrkesgrupp-control-'+index"
|
||||
[uiOptions]="yrkesgruppSelectOptions"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid('yrkesgrupp', index)"
|
||||
[uiValidationMessage]="formControlError('yrkesgrupp', index)"
|
||||
></ui-select>
|
||||
|
||||
<digi-form-fieldset af-legend="Anställningsform">
|
||||
<ui-radiobutton-group
|
||||
[uiRadiobuttons]="anstallningsformOptions"
|
||||
formControlName="anstallningsform"
|
||||
[uiName]="'anstallningsform-'+index"
|
||||
[uiId]="'slutredovisning-anstallningsform-control-'+index"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid('anstallningsform', index)"
|
||||
[uiValidationMessage]="formControlError('anstallningsform', index)"
|
||||
></ui-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<ui-textarea
|
||||
*ngIf="formArray.controls[index].get('anstallningsform').value === Anstallningsform.Annat"
|
||||
formControlName="annatAnstallningComment"
|
||||
uiLabel="Beskrivning av Annat"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiId]="'slutredovisning-annatAnstallningComment-control-'+index"
|
||||
[uiMaxLength]="200"
|
||||
[uiInvalid]="formControlIsInvalid('annatAnstallningComment', index)"
|
||||
[uiValidationMessage]="formControlError('annatAnstallningComment', index)"
|
||||
></ui-textarea>
|
||||
|
||||
<digi-form-fieldset af-legend="Omfattning">
|
||||
<div class="slutredovisning-form-step0-work__fieldset-content">
|
||||
<ui-radiobutton-group
|
||||
#omfattningRadioGroup
|
||||
[uiRadiobuttons]="omfattningOptions"
|
||||
formControlName="omfattning"
|
||||
[uiName]="'omfattning-'+index"
|
||||
[uiId]="'slutredovisning-omfattning-control-'+index"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid('omfattning', index)"
|
||||
[uiValidationMessage]="formControlError('omfattning', index)"
|
||||
></ui-radiobutton-group>
|
||||
<ui-input
|
||||
*ngIf="formArray.controls[index].get('omfattning').value === Omfattning.Deltid"
|
||||
class="slutredovisning-form-step0-work__number-input"
|
||||
formControlName="omfattningPercent"
|
||||
uiType="number"
|
||||
[uiMin]="1"
|
||||
[uiMax]="99"
|
||||
uiLabel="Anställningens omfattning i procent"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid('omfattningPercent', index)"
|
||||
[uiValidationMessage]="formControlError('omfattningPercent', index)"
|
||||
></ui-input>
|
||||
</div>
|
||||
</digi-form-fieldset>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
@@ -0,0 +1,30 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-form-step0-work {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
|
||||
&__group,
|
||||
&__fieldset-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter;
|
||||
}
|
||||
|
||||
&__group-header {
|
||||
display: flex;
|
||||
gap: $digi--layout--gutter;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__heading {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__number-input {
|
||||
::ng-deep input {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SlutredovisningFormStep0WorkComponent } from './slutredovisning-form-step0-work.component';
|
||||
|
||||
describe('SlutredovisningFormStep0WorkComponent', () => {
|
||||
let component: SlutredovisningFormStep0WorkComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep0WorkComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep0WorkComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep0WorkComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { FormArray, FormControl, FormGroup } from '@angular/forms';
|
||||
import { Anstallningsform, MainOccupation, Omfattning } from '@msfa-models/slutredovisning.model';
|
||||
import { Yrkesgrupp } from '@msfa-models/yrkesgrupp.model';
|
||||
import { Yrkesomrade } from '@msfa-models/yrkesomrade.model';
|
||||
import { capitalizeSentence } from '@msfa-utils/capitalize-sentence.util';
|
||||
import { ErrorLink } from '@ui/error-list/error-link.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { SlutredovisningFormService } from '../../slutredovisning-form.service';
|
||||
import { SlutredovisningFormStep0WorkFormData } from './slutredovisning-form-step0-work.model';
|
||||
import { SlutredovisningFormStep0WorkValidator } from './slutredovisning-form-step0-work.validator';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step0-work',
|
||||
templateUrl: './slutredovisning-form-step0-work.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step0-work.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep0WorkComponent implements OnInit {
|
||||
readonly formGroupKey: MainOccupation = MainOccupation.Work;
|
||||
@Input() formGroupRef: FormGroup;
|
||||
@Input() formData: SlutredovisningFormStep0WorkFormData;
|
||||
@Input() shouldValidate: boolean;
|
||||
MainOccupation = MainOccupation;
|
||||
Anstallningsform = Anstallningsform;
|
||||
Omfattning = Omfattning;
|
||||
|
||||
yrkesomradeSelectOptions$: Observable<Yrkesomrade[]> = this.slutredovisningFormService.yrkesomraden$;
|
||||
formArray: FormArray;
|
||||
|
||||
anstallningsformOptions: RadiobuttonModel[] = Object.values(Anstallningsform).map(value => ({
|
||||
label: capitalizeSentence(value),
|
||||
value,
|
||||
}));
|
||||
omfattningOptions: RadiobuttonModel[] = Object.values(Omfattning).map(value => ({
|
||||
label: capitalizeSentence(value),
|
||||
value,
|
||||
}));
|
||||
|
||||
constructor(private slutredovisningFormService: SlutredovisningFormService) {}
|
||||
|
||||
getYrkesgruppSelectOptions$(index: number): Observable<Yrkesgrupp[]> {
|
||||
const yrkesomradeId = this.formArray.controls[index].get('yrkesomrade').value as string;
|
||||
|
||||
return this.yrkesomradeSelectOptions$.pipe(
|
||||
map(yrkesomraden =>
|
||||
yrkesomradeId
|
||||
? yrkesomraden
|
||||
.filter(yo => yo.value === yrkesomradeId)
|
||||
.reduce((yrkesgrupper: Yrkesgrupp[], yo: Yrkesomrade) => {
|
||||
yrkesgrupper.push(...yo.items);
|
||||
return yrkesgrupper;
|
||||
}, [])
|
||||
: null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
get errorListErrors(): ErrorLink[] {
|
||||
const formControlErrors: ErrorLink[] = [];
|
||||
if (this.shouldValidate) {
|
||||
this.formArray.controls.forEach(arrayControl => {
|
||||
if (arrayControl.errors) {
|
||||
Object.entries(arrayControl.errors).forEach(([key, value]) => {
|
||||
formControlErrors.push({
|
||||
elementId: `slutredovisning-${key}-control`,
|
||||
text: value as string,
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return formControlErrors;
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControlName: string, index: number): boolean {
|
||||
const errors = this.formArray.controls[index].errors;
|
||||
return errors && errors[formControlName] && this.shouldValidate;
|
||||
}
|
||||
|
||||
formControlError(formControlName: string, index: number): string {
|
||||
const errors = this.formArray.controls[index].errors;
|
||||
return errors && (errors[formControlName] as string);
|
||||
}
|
||||
|
||||
addNewFormGroupToFormArray(): void {
|
||||
this.formArray.push(
|
||||
new FormGroup(
|
||||
{
|
||||
yrkesomrade: new FormControl(null),
|
||||
yrkesgrupp: new FormControl(null),
|
||||
anstallningsform: new FormControl(null),
|
||||
annatAnstallningComment: new FormControl(null),
|
||||
omfattning: new FormControl(null),
|
||||
omfattningPercent: new FormControl(null),
|
||||
},
|
||||
[SlutredovisningFormStep0WorkValidator.slutredovisningFormStep0WorkIsValid()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
removeGroup(index: number): void {
|
||||
this.formArray.removeAt(index);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.formGroupRef.get(this.formGroupKey)) {
|
||||
this.formArray = this.formGroupRef.get(this.formGroupKey) as FormArray;
|
||||
} else {
|
||||
this.formArray = new FormArray([]);
|
||||
this.addNewFormGroupToFormArray();
|
||||
this.formGroupRef.addControl(this.formGroupKey, this.formArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface SlutredovisningFormStep0WorkFormData {
|
||||
yrkesomrade: string;
|
||||
yrkesgrupp: string;
|
||||
anstallningsform: string;
|
||||
otherExplanation: string;
|
||||
omfattning: string;
|
||||
omfattningPercent: number;
|
||||
}
|
||||
|
||||
export type FormKeys = keyof SlutredovisningFormStep0WorkFormData;
|
||||
@@ -0,0 +1,69 @@
|
||||
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||
import { Anstallningsform, Omfattning } from '@msfa-models/slutredovisning.model';
|
||||
import { SlutredovisningFormStep0WorkErrors } from './slutredovisning-form-step0-work-errors.model';
|
||||
import { SlutredovisningFormStep0WorkFormData } from './slutredovisning-form-step0-work.model';
|
||||
|
||||
export class SlutredovisningFormStep0WorkValidator {
|
||||
static slutredovisningFormStep0WorkIsValid(): ValidatorFn {
|
||||
return (c: AbstractControl): SlutredovisningFormStep0WorkErrors => {
|
||||
let errors: SlutredovisningFormStep0WorkErrors = {};
|
||||
const {
|
||||
yrkesomrade,
|
||||
yrkesgrupp,
|
||||
anstallningsform,
|
||||
otherExplanation,
|
||||
omfattning,
|
||||
omfattningPercent,
|
||||
} = c.value as SlutredovisningFormStep0WorkFormData;
|
||||
|
||||
if (!yrkesomrade) {
|
||||
errors = {
|
||||
...errors,
|
||||
yrkesomrade: 'Yrkesområde måste väljas',
|
||||
};
|
||||
}
|
||||
if (yrkesomrade && !yrkesgrupp) {
|
||||
errors = {
|
||||
...errors,
|
||||
yrkesgrupp: 'Yrkesgrupp måste väljas',
|
||||
};
|
||||
}
|
||||
if (!anstallningsform) {
|
||||
errors = {
|
||||
...errors,
|
||||
anstallningsform: 'Anställningsform måste väljas',
|
||||
};
|
||||
} else if (anstallningsform === Anstallningsform.Annat && !otherExplanation) {
|
||||
errors = {
|
||||
...errors,
|
||||
annatAnstallningComment: 'Beskrivning av annat är obligatorisk om "annat" är vald under anställningsform',
|
||||
};
|
||||
}
|
||||
if (!omfattning) {
|
||||
errors = {
|
||||
...errors,
|
||||
omfattning: 'Omfattning måste väljas',
|
||||
};
|
||||
} else if (omfattning === Omfattning.Deltid) {
|
||||
if (omfattningPercent === undefined || omfattningPercent === null) {
|
||||
errors = {
|
||||
...errors,
|
||||
omfattningPercent: 'Anställningens omfattning i procent är obligatoriskt',
|
||||
};
|
||||
} else if (omfattningPercent < 1) {
|
||||
errors = {
|
||||
...errors,
|
||||
omfattningPercent: 'Anställningens omfattning i procent får inte vara mindre än 1% om deltid har valts',
|
||||
};
|
||||
} else if (omfattningPercent > 99) {
|
||||
errors = {
|
||||
...errors,
|
||||
omfattningPercent: 'Anställningens omfattning i procent får inte vara mer än 99% om deltid har valts',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<h2>Huvudsaklig sysselsättning</h2>
|
||||
|
||||
<form
|
||||
class="slutredovisning-form-step0"
|
||||
[formGroup]="formGroup"
|
||||
name="slutredovisning-form-step0"
|
||||
(ngSubmit)="verifyAndEmitData()"
|
||||
>
|
||||
<digi-form-fieldset
|
||||
af-legend="Vilken är deltagarens huvudsakliga sysselsättning nu?"
|
||||
af-name="mainOccupation"
|
||||
af-form="slutredovisning-form-step0"
|
||||
>
|
||||
<ui-radiobutton-group
|
||||
uiId="slutredovisning-mainOccupation"
|
||||
[uiRadiobuttons]="mainOccupationRadioButtonGroup"
|
||||
formControlName="mainOccupation"
|
||||
[uiRequired]="true"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiInvalid]="formControlIsInvalid(mainOccupationFormControl)"
|
||||
[uiValidationMessage]="mainOccupationFormControl.errors?.required"
|
||||
></ui-radiobutton-group>
|
||||
</digi-form-fieldset>
|
||||
|
||||
<msfa-slutredovisning-form-step0-education
|
||||
*ngIf="mainOccupationFormControl.value === MainOccupation.Education"
|
||||
[formGroupRef]="formGroup"
|
||||
[shouldValidate]="shouldValidate$ | async"
|
||||
></msfa-slutredovisning-form-step0-education>
|
||||
|
||||
<msfa-slutredovisning-form-step0-still-unemployed
|
||||
*ngIf="mainOccupationFormControl.value === MainOccupation.StillUnemployed"
|
||||
[formGroupRef]="formGroup"
|
||||
[shouldValidate]="shouldValidate$ | async"
|
||||
></msfa-slutredovisning-form-step0-still-unemployed>
|
||||
|
||||
<msfa-slutredovisning-form-step0-work
|
||||
*ngIf="mainOccupationFormControl.value === MainOccupation.Work"
|
||||
[formGroupRef]="formGroup"
|
||||
[shouldValidate]="shouldValidate$ | async"
|
||||
></msfa-slutredovisning-form-step0-work>
|
||||
|
||||
<msfa-slutredovisning-form-step0-other
|
||||
*ngIf="mainOccupationFormControl.value === MainOccupation.Other"
|
||||
[formGroupRef]="formGroup"
|
||||
[shouldValidate]="shouldValidate$ | async"
|
||||
></msfa-slutredovisning-form-step0-other>
|
||||
|
||||
<digi-button af-type="submit">Vidare till steg 2</digi-button>
|
||||
</form>
|
||||
@@ -0,0 +1,7 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-form-step0 {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep0Component } from './slutredovisning-form-step0.component';
|
||||
|
||||
describe('SlutredovisningFormStep1Component', () => {
|
||||
let component: SlutredovisningFormStep0Component;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep0Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep0Component],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep0Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { markControlsAsDirty } from '@msfa-utils/mark-controls-as-dirty.util';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SlutredovisningFormStep0EducationFormData } from './slutredovisning-form-step0-education/slutredovisning-form-step0-education.component';
|
||||
import { SlutredovisningFormStep0WorkFormData } from './slutredovisning-form-step0-work/slutredovisning-form-step0-work.model';
|
||||
import { SlutredovisningFormStep0StillUnemployedFormData } from './slutredovisning-form-step0-still-unemployed/slutredovisning-form-step0-still-unemployed.component';
|
||||
import { SlutredovisningFormStep0OtherFormData } from './slutredovisning-form-step0-other/slutredovisning-form-step0-other.component';
|
||||
import { MainOccupation } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningStep0FormData {
|
||||
mainOccupation: MainOccupation;
|
||||
education: SlutredovisningFormStep0EducationFormData;
|
||||
work: SlutredovisningFormStep0WorkFormData[];
|
||||
stillUnemployed: SlutredovisningFormStep0StillUnemployedFormData;
|
||||
other: SlutredovisningFormStep0OtherFormData;
|
||||
}
|
||||
|
||||
interface MainOccupationWithLabel {
|
||||
label: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
type FormKeys = keyof SlutredovisningStep0FormData;
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step0',
|
||||
templateUrl: './slutredovisning-form-step0.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step0.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep0Component implements OnInit {
|
||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||
@Output() nextClick = new EventEmitter<SlutredovisningStep0FormData>();
|
||||
@Input() formGroup: FormGroup;
|
||||
MainOccupation = MainOccupation;
|
||||
|
||||
private mainOccupationFormControlName: FormKeys = 'mainOccupation';
|
||||
|
||||
mainOccupations: MainOccupationWithLabel[] = [
|
||||
{ label: 'Arbete', key: MainOccupation.Work },
|
||||
{ label: 'Utbildning', key: MainOccupation.Education },
|
||||
{ label: 'Fortsatt arbetssökande', key: MainOccupation.StillUnemployed },
|
||||
{
|
||||
label: 'Byte till ny leverantör i rusta och matcha',
|
||||
key: MainOccupation.ByteTillNyLeverantorIRustaOchMatcha,
|
||||
},
|
||||
{ label: 'Annat', key: MainOccupation.Other },
|
||||
];
|
||||
|
||||
get mainOccupationRadioButtonGroup(): RadiobuttonModel[] {
|
||||
return this.mainOccupations.map(mainOccupation => ({ label: mainOccupation.label, value: mainOccupation.key }));
|
||||
}
|
||||
|
||||
get mainOccupationFormControl(): AbstractControl | undefined {
|
||||
return this.formGroup.get(this.mainOccupationFormControlName);
|
||||
}
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
|
||||
}
|
||||
|
||||
verifyAndEmitData(): void {
|
||||
this.mainOccupations
|
||||
.map(mainOccupations => mainOccupations.key)
|
||||
.filter(formGroupKey => this.mainOccupationFormControl.value !== formGroupKey)
|
||||
.forEach(notSelectedFormGroupKey => this.formGroup.removeControl(notSelectedFormGroupKey));
|
||||
|
||||
this.shouldValidate$.next(true);
|
||||
markControlsAsDirty(Object.values(this.formGroup.controls));
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.valid) {
|
||||
this.nextClick.emit(this.formGroup.value);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.mainOccupationFormControl) {
|
||||
this.formGroup.addControl(
|
||||
this.mainOccupationFormControlName,
|
||||
new FormControl(null, [RequiredValidator('Val av huvudsysselsättning är obligatoriskt')])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<h2>Aktiviteter</h2>
|
||||
<form [formGroup]="formGroup" name="report-form" (ngSubmit)="verifyAndEmitData()">
|
||||
<ng-container *ngIf="activitiesFormArray.controls.length; else loadingRef" [formArrayName]="ACTIVITES_FORM_NAME">
|
||||
<div
|
||||
class="slutredovisning-form__activity"
|
||||
*ngFor="let activityFormGroup of activitiesFormArray.controls; let i=index"
|
||||
[formGroupName]="i"
|
||||
>
|
||||
<h3>{{activityFormGroup.get('name').value}}</h3>
|
||||
<ui-textarea
|
||||
formControlName="whatHasBeenDone"
|
||||
uiLabel="Beskriv vad deltagaren har gjort inom aktiviteten"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiMaxLength]="2000"
|
||||
[uiMinLength]="1"
|
||||
[uiRequired]="true"
|
||||
[uiValidationMessage]="activityFormGroup.get('description')?.errors?.required"
|
||||
[uiInvalid]="activityFormGroup.get('description')?.touched && !!activityFormGroup.get('description')?.errors?.required"
|
||||
></ui-textarea>
|
||||
</div>
|
||||
<!-- TODO: If something is required at the top of the form it will be hard to see when at the bottom and clicking next -->
|
||||
|
||||
<footer class="slutredovisning-report-form-step1__footer">
|
||||
<digi-button af-type="button" af-variation="tertiary" (click)="goBack()">
|
||||
<digi-icon-arrow-left slot="icon"></digi-icon-arrow-left>
|
||||
Tillbaka
|
||||
</digi-button>
|
||||
<digi-button af-type="submit">Nästa</digi-button>
|
||||
</footer>
|
||||
</ng-container>
|
||||
</form>
|
||||
|
||||
<ng-template #loadingRef>
|
||||
<ui-loader uiType="padded"></ui-loader>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,6 @@
|
||||
.slutredovisning-report-form-step1 {
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep1Component } from './slutredovisning-form-step1.component';
|
||||
|
||||
describe('SlutredovisningFormStep1Component', () => {
|
||||
let component: SlutredovisningFormStep1Component;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep1Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep1Component],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep1Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { markControlsAsDirty } from '@msfa-utils/mark-controls-as-dirty.util';
|
||||
import { Activity } from '@msfa-models/activity.model';
|
||||
import { SlutredovisningFormService } from '../slutredovisning-form.service';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
|
||||
export interface SlutredovisningStep1FormData {
|
||||
activities: { whatHasBeenDone: string; id: string; name: string }[];
|
||||
}
|
||||
|
||||
type FormControlNames = keyof SlutredovisningStep1FormData;
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step1',
|
||||
templateUrl: './slutredovisning-form-step1.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step1.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep1Component implements OnInit {
|
||||
readonly ACTIVITES_FORM_NAME: FormControlNames = 'activities';
|
||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||
@Output() nextClick = new EventEmitter<SlutredovisningStep1FormData>();
|
||||
@Output() backClick = new EventEmitter<void>();
|
||||
@Input() genomforandereferens: number;
|
||||
@Input() formGroup: FormGroup;
|
||||
|
||||
constructor(
|
||||
private slutredovisningFormService: SlutredovisningFormService,
|
||||
private changeDetectionRef: ChangeDetectorRef
|
||||
) {}
|
||||
|
||||
private clearActivities(): void {
|
||||
this.activitiesFormArray.clear();
|
||||
}
|
||||
|
||||
private addActivityToForm(activity: Activity): void {
|
||||
this.activitiesFormArray.push(
|
||||
new FormGroup({
|
||||
name: new FormControl(activity.name),
|
||||
id: new FormControl(activity.id),
|
||||
whatHasBeenDone: new FormControl('temp', RequiredValidator('Beskrivning är obligatorisk')),
|
||||
})
|
||||
);
|
||||
this.changeDetectionRef.detectChanges();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.activitiesFormArray) {
|
||||
this.formGroup.addControl(this.ACTIVITES_FORM_NAME, new FormArray([]));
|
||||
|
||||
this.slutredovisningFormService
|
||||
.getAllChosenActivities(this.genomforandereferens)
|
||||
.pipe(take(1))
|
||||
.subscribe(activities => {
|
||||
this.clearActivities();
|
||||
activities.forEach(activity => this.addActivityToForm(activity));
|
||||
});
|
||||
}
|
||||
}
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
|
||||
}
|
||||
|
||||
get activitiesFormArray(): FormArray {
|
||||
return this.formGroup.get(this.ACTIVITES_FORM_NAME) as FormArray;
|
||||
}
|
||||
|
||||
verifyAndEmitData(): void {
|
||||
this.shouldValidate$.next(true);
|
||||
markControlsAsDirty(Object.values(this.formGroup.controls));
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.valid) {
|
||||
this.nextClick.emit(this.formGroup.value);
|
||||
}
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.backClick.emit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<h2>Deltagarens framsteg och utveckling</h2>
|
||||
<form [formGroup]="formGroup" name="slutredovisning-report-form-step1__form" (ngSubmit)="verifyAndEmitData()">
|
||||
<ui-textarea
|
||||
[formControl]="framstegFormControl"
|
||||
uiLabel="Beskriv deltagarens framsteg och utveckling under perioden"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiMaxLength]="2000"
|
||||
[uiMinLength]="1"
|
||||
[uiRequired]="true"
|
||||
[uiValidationMessage]="framstegFormControl.errors?.required"
|
||||
[uiInvalid]="framstegFormControl?.touched && !!framstegFormControl?.errors?.required"
|
||||
></ui-textarea>
|
||||
|
||||
<ui-textarea
|
||||
[formControl]="nastaStegFormControl"
|
||||
uiLabel="Information om lämpligt nästa steg för deltagaren"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiMaxLength]="2000"
|
||||
[uiMinLength]="1"
|
||||
[uiRequired]="true"
|
||||
[uiValidationMessage]="nastaStegFormControl.errors?.required"
|
||||
[uiInvalid]="nastaStegFormControl?.touched && !!nastaStegFormControl?.errors?.required"
|
||||
></ui-textarea>
|
||||
|
||||
<ui-textarea
|
||||
[formControl]="ovrigtFormControl"
|
||||
uiLabel="Övrig information"
|
||||
[uiAnnounceIfOptional]="true"
|
||||
[uiMaxLength]="2000"
|
||||
[uiMinLength]="1"
|
||||
[uiRequired]="true"
|
||||
[uiValidationMessage]="ovrigtFormControl.errors?.required"
|
||||
[uiInvalid]="ovrigtFormControl?.touched && !!ovrigtFormControl?.errors?.required"
|
||||
></ui-textarea>
|
||||
|
||||
<footer class="slutredovisning-report-form-step1__footer">
|
||||
<digi-button af-type="button" af-variation="tertiary" (click)="goBack()">
|
||||
<digi-icon-arrow-left slot="icon"></digi-icon-arrow-left>
|
||||
Tillbaka
|
||||
</digi-button>
|
||||
<digi-button af-type="submit">Nästa</digi-button>
|
||||
</footer>
|
||||
</form>
|
||||
|
||||
<ng-template #loadingRef>
|
||||
<ui-loader uiType="padded"></ui-loader>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,13 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-report-form-step1 {
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
}
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep2Component } from './slutredovisning-form-step2.component';
|
||||
|
||||
describe('SlutredovisningFormStep1Component', () => {
|
||||
let component: SlutredovisningFormStep2Component;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep2Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [SlutredovisningFormStep2Component],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep2Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { markControlsAsDirty } from '@msfa-utils/mark-controls-as-dirty.util';
|
||||
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||
|
||||
export interface SlutredovisningStep2FormData {
|
||||
progressDescription: string;
|
||||
nextStepDescription: string;
|
||||
otherInformation: string;
|
||||
}
|
||||
|
||||
type FormControlNames = keyof SlutredovisningStep2FormData;
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step2',
|
||||
templateUrl: './slutredovisning-form-step2.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step2.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep2Component implements OnInit {
|
||||
readonly framstegFormName: FormControlNames = 'progressDescription';
|
||||
readonly nastaStegFormName: FormControlNames = 'nextStepDescription';
|
||||
readonly ovrigtFormName: FormControlNames = 'otherInformation';
|
||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||
@Output() nextClick = new EventEmitter<SlutredovisningStep2FormData>();
|
||||
@Output() backClick = new EventEmitter<void>();
|
||||
@Input() formGroup: FormGroup;
|
||||
@Input() genomforandereferens: number;
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
|
||||
}
|
||||
|
||||
get framstegFormControl(): FormControl {
|
||||
return this.formGroup.get(this.framstegFormName) as FormControl;
|
||||
}
|
||||
get nastaStegFormControl(): FormControl {
|
||||
return this.formGroup.get(this.nastaStegFormName) as FormControl;
|
||||
}
|
||||
get ovrigtFormControl(): FormControl {
|
||||
return this.formGroup.get(this.ovrigtFormName) as FormControl;
|
||||
}
|
||||
|
||||
verifyAndEmitData(): void {
|
||||
this.shouldValidate$.next(true);
|
||||
markControlsAsDirty(Object.values(this.formGroup.controls));
|
||||
this.formGroup.markAllAsTouched();
|
||||
|
||||
if (this.formGroup.valid) {
|
||||
this.nextClick.emit(this.formGroup.value);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.framstegFormControl) {
|
||||
this.formGroup.addControl(this.framstegFormName, new FormControl(null, RequiredValidator()));
|
||||
this.formGroup.addControl(this.nastaStegFormName, new FormControl(null, RequiredValidator()));
|
||||
this.formGroup.addControl(this.ovrigtFormName, new FormControl(null, RequiredValidator()));
|
||||
}
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.backClick.emit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<div class="slutredovisning-report-form-step3__view">
|
||||
<h2>Förhandsgranskning</h2>
|
||||
|
||||
<div class="informativ-rapport-form__confirmation" *ngIf="submittedDate$ | async as submittedDate; else previewRef">
|
||||
<digi-notification-alert af-variation="success" af-heading="Allt gick bra" af-heading-level="h3">
|
||||
<p>Slutredovisning för deltagare {{avrop.fullName}} är nu inskickad till Arbetsförmedlingen.</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 #previewRef>
|
||||
<ui-loader *ngIf="submitIsLoading$ | async" uiType="absolute"></ui-loader>
|
||||
<msfa-slutredovisning-view-description-list
|
||||
[slutredovisning]="slutredovisning$ | async"
|
||||
></msfa-slutredovisning-view-description-list>
|
||||
|
||||
<digi-notification-alert
|
||||
*ngIf="submitError$ | async as error"
|
||||
class="slutredovisnin-rapport-form__alert"
|
||||
af-variation="danger"
|
||||
af-heading="Någonting gick fel"
|
||||
>
|
||||
<p>Kunde inte skicka in Slutredovisning. Försök igen om en stund.</p>
|
||||
<p *ngIf="error.message" class="msfa__small-text">{{error.message}}</p>
|
||||
</digi-notification-alert>
|
||||
|
||||
<footer class="slutredovisning-report-form-step3__footer">
|
||||
<digi-button af-type="button" af-variation="tertiary" (click)="goBack()">
|
||||
<digi-icon-arrow-left slot="icon"></digi-icon-arrow-left>
|
||||
Tillbaka
|
||||
</digi-button>
|
||||
<digi-button af-type="button" (click)="submitSlutredovisning()">Skicka in Slutredovisning</digi-button>
|
||||
</footer>
|
||||
</ng-template>
|
||||
</div>
|
||||
<!--<ui-loader *ngIf="submitIsLoading$ | async" uiType="absolute"></ui-loader>-->
|
||||
<!--<msfa-report-description-list [avrop]="avrop">-->
|
||||
<!-- <dt>Orsak till slutredovisning:</dt>-->
|
||||
<!-- <dd>bbbb</dd>-->
|
||||
<!--</msfa-report-description-list>-->
|
||||
<!--<digi-notification-alert-->
|
||||
<!-- *ngIf="submitError$ | async as error"-->
|
||||
<!-- af-variation="danger"-->
|
||||
<!-- af-heading="Någonting gick fel"-->
|
||||
<!-->-->
|
||||
<!-- <p>Kunde inte spara slutredovisning. Ladda om sidan och försök igen.</p>-->
|
||||
<!-- <p *ngIf="error.message" class="msfa__small-text">{{error.message}}</p>-->
|
||||
<!--</digi-notification-alert>-->
|
||||
@@ -0,0 +1,18 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-report-form-step3 {
|
||||
&__view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
}
|
||||
//&__form {
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// gap: $digi--layout--gutter--l;
|
||||
//}
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SlutredovisningFormStep3Component } from './slutredovisning-form-step3.component';
|
||||
|
||||
describe('SlutredovisningFormStep3Component', () => {
|
||||
let component: SlutredovisningFormStep3Component;
|
||||
let fixture: ComponentFixture<SlutredovisningFormStep3Component>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SlutredovisningFormStep3Component ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormStep3Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { slutredovisningFormDataToSlutredovisningRequest } from '../utils/form-data-to-slutredovisning.util';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SlutredovisningFormService } from '../slutredovisning-form.service';
|
||||
import { SlutredovisningResponseMainOccupationDetails } from '@msfa-models/api/slutredovisning.response.model';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { MainOccupation, Slutredovisning } from '@msfa-models/slutredovisning.model';
|
||||
import { SlutredovisningFormData } from '../models/slutredovisning-form-data.model';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form-step3',
|
||||
templateUrl: './slutredovisning-form-step3.component.html',
|
||||
styleUrls: ['./slutredovisning-form-step3.component.scss'],
|
||||
})
|
||||
export class SlutredovisningFormStep3Component {
|
||||
@Output() backClick = new EventEmitter<void>();
|
||||
@Input() slutredovisningFormData: SlutredovisningFormData;
|
||||
@Input() avrop: Avrop;
|
||||
|
||||
submitIsLoading$ = new BehaviorSubject<boolean>(false);
|
||||
submitError$ = new BehaviorSubject<CustomError>(null);
|
||||
submittedDate$ = new BehaviorSubject<Date | null>(null);
|
||||
|
||||
slutredovisning$ = this.slutredovisningFormService.yrkeToTextMap$.pipe(
|
||||
map(yrkeToText => this._appendYrkeNames(this.slutredovisningFormData, yrkeToText))
|
||||
);
|
||||
|
||||
private _appendYrkeNames(
|
||||
slutredovisningFormData: SlutredovisningFormData,
|
||||
yrkeToText: { [key: string]: string }
|
||||
): Slutredovisning {
|
||||
if (slutredovisningFormData.mainOccupation.type !== MainOccupation.Work) {
|
||||
return slutredovisningFormData as Slutredovisning;
|
||||
}
|
||||
|
||||
const newMainOccupation: SlutredovisningResponseMainOccupationDetails = {
|
||||
...slutredovisningFormData.mainOccupation,
|
||||
work: slutredovisningFormData.mainOccupation.work.map(yrke => ({
|
||||
...yrke,
|
||||
yrkesomradeName: yrkeToText[yrke.yrkesomrade],
|
||||
yrkesgruppName: yrkeToText[yrke.yrkesgrupp],
|
||||
})),
|
||||
};
|
||||
return { ...slutredovisningFormData, mainOccupation: newMainOccupation };
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.backClick.emit();
|
||||
}
|
||||
|
||||
constructor(private slutredovisningFormService: SlutredovisningFormService) {}
|
||||
|
||||
submitSlutredovisning(): void {
|
||||
this.submitIsLoading$.next(true);
|
||||
|
||||
const toBeSubmitted = slutredovisningFormDataToSlutredovisningRequest(this.slutredovisningFormData);
|
||||
this.slutredovisningFormService.submitSlutredovisning$(toBeSubmitted).subscribe({
|
||||
next: () => {
|
||||
this.submitIsLoading$.next(false);
|
||||
this.submittedDate$.next(new Date());
|
||||
},
|
||||
error: (customError: CustomError) => {
|
||||
this.submitError$.next({ ...customError, message: customError.error.message });
|
||||
this.submitIsLoading$.next(false);
|
||||
throw { ...customError, avoidToast: true };
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<msfa-layout>
|
||||
<msfa-report-layout
|
||||
*ngIf="avrop$ | async as avrop; else skeletonRef"
|
||||
[avrop]="avrop$ | async"
|
||||
[description]="(showAvropDetails$ | async) ? 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet autem iusto natus officiis rerum ullam! Aliquam earum expedita hic in numquam porro totam ullam ut. Et harum in porro quae!' : ''"
|
||||
reportTitle="Slutredovisning"
|
||||
[showAvropDetails]="showAvropDetails$ | async"
|
||||
>
|
||||
<div class="slutredovisning-form">
|
||||
<div class="slutredovisning-form__warning" *ngIf="!isAllowedToReport(avrop); else reportRef">
|
||||
<digi-notification-alert af-variation="warning" af-heading="Kan inte skapa slutredovisning">
|
||||
<p>{{notAllowedToReportWarning(avrop)}}</p>
|
||||
</digi-notification-alert>
|
||||
|
||||
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>
|
||||
</div>
|
||||
<ng-template #reportRef>
|
||||
<!-- <digi-progressbar-->
|
||||
<!-- [afTotalSteps]="totalSteps"-->
|
||||
<!-- [afCompletedSteps]="getStepIndex(currentStep$ | async)"-->
|
||||
<!-- af-variation="{${ProgressbarVariation.PRIMARY}}"-->
|
||||
<!-- ></digi-progressbar>-->
|
||||
|
||||
<ng-container [ngSwitch]="currentStep$ | async">
|
||||
<msfa-slutredovisning-form-step0
|
||||
*ngSwitchCase="'step0'"
|
||||
(nextClick)="processStep0Data($event)"
|
||||
[formGroup]="step0FormGroup"
|
||||
></msfa-slutredovisning-form-step0>
|
||||
<msfa-slutredovisning-form-step1
|
||||
*ngSwitchCase="'step1'"
|
||||
[genomforandereferens]="avrop.genomforandeReferens"
|
||||
(nextClick)="processStep1Data($event)"
|
||||
(backClick)="backFromStep1()"
|
||||
[formGroup]="step1FormGroup"
|
||||
></msfa-slutredovisning-form-step1>
|
||||
<msfa-slutredovisning-form-step2
|
||||
*ngSwitchCase="'step2'"
|
||||
[genomforandereferens]="avrop.genomforandeReferens"
|
||||
(nextClick)="processStep2Data($event)"
|
||||
(backClick)="backFromStep2()"
|
||||
[formGroup]="step2FormGroup"
|
||||
></msfa-slutredovisning-form-step2>
|
||||
<msfa-slutredovisning-form-step3
|
||||
*ngSwitchCase="'step3'"
|
||||
[slutredovisningFormData]="slutredovisningFormData$ | async"
|
||||
[avrop]="avrop$ | async"
|
||||
(backClick)="backFromStep3()"
|
||||
></msfa-slutredovisning-form-step3>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
<!-- <h2>Step 0 valueChanges:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{ step0FormGroup.valueChanges | async | json}}-->
|
||||
<!-- </pre>-->
|
||||
<!-- <h2>Step 1 valueChanges:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{ step1FormGroup.valueChanges | async | json}}-->
|
||||
<!-- </pre>-->
|
||||
<!-- <h2>Step 2 valueChanges:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{ step2FormGroup.valueChanges | async | json}}-->
|
||||
<!-- </pre>-->
|
||||
|
||||
<!-- <h2>Step 0 data:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{step0FormData$ | async | json}}-->
|
||||
<!-- </pre-->
|
||||
<!-- >-->
|
||||
|
||||
<!-- <h2>Step 1 data:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{step1FormData$ | async | json}}-->
|
||||
<!-- </pre-->
|
||||
<!-- >-->
|
||||
<!-- <h2>Step 2 data:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{step2FormData$ | async | json}}-->
|
||||
<!-- </pre-->
|
||||
<!-- >-->
|
||||
<!-- <h2>requestData$ data:</h2>-->
|
||||
<!-- <pre>-->
|
||||
<!-- {{slutredovisning$ | async | json}}-->
|
||||
<!-- </pre-->
|
||||
<!-- >-->
|
||||
</msfa-report-layout>
|
||||
</msfa-layout>
|
||||
<ng-template #skeletonRef>
|
||||
<ui-skeleton [uiCount]="3" uiText="Laddar data för att kunna skapa slutredovisning"></ui-skeleton>
|
||||
</ng-template>
|
||||
<ng-template #loadingRef>
|
||||
<ui-loader uiType="padded"></ui-loader>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,19 @@
|
||||
@import 'variables/gutters';
|
||||
|
||||
.slutredovisning-form {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
|
||||
&__confirmation,
|
||||
&__textareas,
|
||||
&__warning,
|
||||
&__form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
}
|
||||
|
||||
&__cta-wrapper {
|
||||
display: flex;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
||||
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
|
||||
import { SlutredovisningFormComponent } from './slutredovisning-form.component';
|
||||
import { SlutredovisningFormService } from './slutredovisning-form.service';
|
||||
|
||||
describe('SlutredovisningReportFormComponent', () => {
|
||||
let component: SlutredovisningFormComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningFormComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [SlutredovisningFormComponent, LayoutComponent],
|
||||
imports: [
|
||||
RouterTestingModule,
|
||||
HttpClientTestingModule,
|
||||
ReactiveFormsModule,
|
||||
DigiNgFormRadiobuttonGroupModule,
|
||||
DigiNgFormDatepickerModule,
|
||||
],
|
||||
providers: [SlutredovisningFormService],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,136 @@
|
||||
import { ChangeDetectionStrategy, Component, OnDestroy } from '@angular/core';
|
||||
import { AbstractControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { SlutredovisningFormService } from './slutredovisning-form.service';
|
||||
import { addDays } from 'date-fns';
|
||||
import { SlutredovisningStep } from './slutredovisning-form.model';
|
||||
import { SlutredovisningStep0FormData } from './slutredovisning-form-step0/slutredovisning-form-step0.component';
|
||||
import { SlutredovisningStep1FormData } from './slutredovisning-form-step1/slutredovisning-form-step1.component';
|
||||
import { SlutredovisningStep2FormData } from './slutredovisning-form-step2/slutredovisning-form-step2.component';
|
||||
import { formsToSlutredovisningFormData } from './utils/form-data-to-slutredovisning.util';
|
||||
import { SlutredovisningFormData } from './models/slutredovisning-form-data.model';
|
||||
|
||||
interface Params {
|
||||
genomforandeReferens: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-form',
|
||||
templateUrl: './slutredovisning-form.component.html',
|
||||
styleUrls: ['./slutredovisning-form.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [SlutredovisningFormService],
|
||||
})
|
||||
export class SlutredovisningFormComponent implements OnDestroy {
|
||||
totalSteps = 4;
|
||||
currentStep$ = this.slutredovisningFormService.currentStep$;
|
||||
showAvropDetails$ = this.currentStep$.pipe(map(step => step === 'step0' || step === 'step3'));
|
||||
|
||||
step0FormGroup: FormGroup = new FormGroup({});
|
||||
step1FormGroup: FormGroup = new FormGroup({});
|
||||
step2FormGroup: FormGroup = new FormGroup({});
|
||||
|
||||
step0FormData$: Observable<SlutredovisningStep0FormData> = this.slutredovisningFormService.step0FormData$;
|
||||
step1FormData$: Observable<SlutredovisningStep1FormData> = this.slutredovisningFormService.step1FormData$;
|
||||
step2FormData$: Observable<SlutredovisningStep2FormData> = this.slutredovisningFormService.step2FormData$;
|
||||
|
||||
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
||||
map((params: Params) => +params.genomforandeReferens)
|
||||
);
|
||||
|
||||
slutredovisningFormData$: Observable<SlutredovisningFormData> = combineLatest([
|
||||
this.genomforandeReferens$,
|
||||
this.step0FormData$,
|
||||
this.step1FormData$,
|
||||
this.step2FormData$,
|
||||
]).pipe(
|
||||
map(([genomforandereferens, step0, step1, step2]) =>
|
||||
formsToSlutredovisningFormData(genomforandereferens, step0, step1, step2)
|
||||
)
|
||||
);
|
||||
|
||||
avrop$: Observable<Avrop> = this.genomforandeReferens$.pipe(
|
||||
switchMap(genomforandeReferens => this.slutredovisningFormService.fetchAvropInformation$(genomforandeReferens)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
private subscriptions: Subscription[] = [];
|
||||
|
||||
constructor(private slutredovisningFormService: SlutredovisningFormService, private activatedRoute: ActivatedRoute) {}
|
||||
|
||||
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
|
||||
}
|
||||
|
||||
openConfirmDialog(): void {
|
||||
this.shouldValidate$.next(true);
|
||||
}
|
||||
|
||||
private _isAfterStartDate(startDate: Date): boolean {
|
||||
return new Date() > startDate;
|
||||
}
|
||||
|
||||
private _isBeforeLastPossibleReportDay(endDate: Date): boolean {
|
||||
const lastPossibleReportDay = addDays(endDate, 5); // Reporting is allowed at latest 5 days past avrop end date.
|
||||
return lastPossibleReportDay > new Date();
|
||||
}
|
||||
|
||||
isAllowedToReport(avrop: Avrop): boolean {
|
||||
return this._isAfterStartDate(avrop.startDate) && this._isBeforeLastPossibleReportDay(avrop.endDate);
|
||||
}
|
||||
|
||||
notAllowedToReportWarning(avrop: Avrop): string {
|
||||
if (!this._isBeforeLastPossibleReportDay(avrop.endDate)) {
|
||||
return 'Det går inte att göra Slutredovisning eftersom tjänsten har avslutats.';
|
||||
}
|
||||
if (!this._isAfterStartDate(avrop.startDate)) {
|
||||
return 'Det går inte att göra Slutredovisning eftersom tjänsten inte har startat ännu.';
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(subscription => subscription.unsubscribe());
|
||||
}
|
||||
|
||||
getStepIndex(step: SlutredovisningStep): number {
|
||||
switch (step) {
|
||||
case 'step0':
|
||||
return 0;
|
||||
case 'step1':
|
||||
return 1;
|
||||
case 'step2':
|
||||
return 2;
|
||||
case 'step3':
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
backFromStep1() {
|
||||
this.slutredovisningFormService.setStep('step0');
|
||||
}
|
||||
|
||||
backFromStep2() {
|
||||
this.slutredovisningFormService.setStep('step1');
|
||||
}
|
||||
|
||||
backFromStep3() {
|
||||
this.slutredovisningFormService.setStep('step2');
|
||||
}
|
||||
|
||||
processStep0Data(formData: SlutredovisningStep0FormData): void {
|
||||
this.slutredovisningFormService.processStep0Data(formData);
|
||||
}
|
||||
|
||||
processStep1Data(formData: SlutredovisningStep1FormData) {
|
||||
this.slutredovisningFormService.processStep1Data(formData);
|
||||
}
|
||||
|
||||
processStep2Data(formData: SlutredovisningStep2FormData) {
|
||||
this.slutredovisningFormService.processStep2Data(formData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export type SlutredovisningStep = 'step0' | 'step1' | 'step2' | 'step3';
|
||||
@@ -0,0 +1,82 @@
|
||||
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
||||
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 { DigiNgFormRangeModule } from '@af/digi-ng/_form/form-range';
|
||||
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||
import { DigiNgProgressProgressbarModule } from '@af/digi-ng/_progress/progressbar';
|
||||
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 { IconModule } from '@msfa-shared/components/icon/icon.module';
|
||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||
import { UiCheckboxModule } from '@ui/checkbox/checkbox.module';
|
||||
import { UiErrorListModule } from '@ui/error-list/error-list.module';
|
||||
import { UiInputModule } from '@ui/input/input.module';
|
||||
import { UiLoaderModule } from '@ui/loader/loader.module';
|
||||
import { UiRadiobuttonGroupModule } from '@ui/radiobutton-group/radiobutton-group.module';
|
||||
import { UiSelectModule } from '@ui/select/select.module';
|
||||
import { UiSkeletonModule } from '@ui/skeleton/skeleton.module';
|
||||
import { UiTextareaModule } from '@ui/textarea/textarea.module';
|
||||
import { ReportDescriptionListModule } from '../../../components/report-description-list/report-description-list.module';
|
||||
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
|
||||
import { SlutredovisningViewDescriptionListModule } from '../../../components/slutredovisning-view-description-list/slutredovisning-view-description-list.module';
|
||||
import { SlutredovisningFormStep0EducationComponent } from './slutredovisning-form-step0/slutredovisning-form-step0-education/slutredovisning-form-step0-education.component';
|
||||
import { SlutredovisningFormStep0OtherComponent } from './slutredovisning-form-step0/slutredovisning-form-step0-other/slutredovisning-form-step0-other.component';
|
||||
import { SlutredovisningFormStep0StillUnemployedComponent } from './slutredovisning-form-step0/slutredovisning-form-step0-still-unemployed/slutredovisning-form-step0-still-unemployed.component';
|
||||
import { SlutredovisningFormStep0WorkComponent } from './slutredovisning-form-step0/slutredovisning-form-step0-work/slutredovisning-form-step0-work.component';
|
||||
import { SlutredovisningFormStep0Component } from './slutredovisning-form-step0/slutredovisning-form-step0.component';
|
||||
import { SlutredovisningFormStep1Component } from './slutredovisning-form-step1/slutredovisning-form-step1.component';
|
||||
import { SlutredovisningFormStep2Component } from './slutredovisning-form-step2/slutredovisning-form-step2.component';
|
||||
import { SlutredovisningFormStep3Component } from './slutredovisning-form-step3/slutredovisning-form-step3.component';
|
||||
import { SlutredovisningFormComponent } from './slutredovisning-form.component';
|
||||
import { SlutredovisningFormService } from './slutredovisning-form.service';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [
|
||||
SlutredovisningFormComponent,
|
||||
SlutredovisningFormStep0Component,
|
||||
SlutredovisningFormStep0EducationComponent,
|
||||
SlutredovisningFormStep0StillUnemployedComponent,
|
||||
SlutredovisningFormStep1Component,
|
||||
SlutredovisningFormStep0WorkComponent,
|
||||
SlutredovisningFormStep1Component,
|
||||
SlutredovisningFormStep0OtherComponent,
|
||||
SlutredovisningFormStep2Component,
|
||||
SlutredovisningFormStep3Component,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([{ path: '', component: SlutredovisningFormComponent }]),
|
||||
UiCheckboxModule,
|
||||
LayoutModule,
|
||||
ReactiveFormsModule,
|
||||
DigiNgFormRadiobuttonGroupModule,
|
||||
UiRadiobuttonGroupModule,
|
||||
DigiNgFormDatepickerModule,
|
||||
UiTextareaModule,
|
||||
DigiNgProgressProgressbarModule,
|
||||
ReportLayoutModule,
|
||||
ConfirmDialogModule,
|
||||
BackLinkModule,
|
||||
UiSkeletonModule,
|
||||
DigiNgFormSelectModule,
|
||||
UiLoaderModule,
|
||||
UiSelectModule,
|
||||
ReportDescriptionListModule,
|
||||
DigiNgFormInputModule,
|
||||
DigiNgDialogModule,
|
||||
DigiNgFormRangeModule,
|
||||
IconModule,
|
||||
UiErrorListModule,
|
||||
UiInputModule,
|
||||
SlutredovisningViewDescriptionListModule,
|
||||
],
|
||||
providers: [SlutredovisningFormService],
|
||||
exports: [SlutredovisningFormComponent],
|
||||
})
|
||||
export class SlutredovisningFormModule {}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Activity, mapResponseToActivity } from '@msfa-models/activity.model';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { mapResponseToYrkesomrade, Yrkesomrade, yrkeToTextMap } from '@msfa-models/yrkesomrade.model';
|
||||
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
import { GemensamPlaneringApiService } from '@msfa-services/api/gemensam-planering-api.service';
|
||||
import { SlutredovisningApiService } from '@msfa-services/api/slutredovisning.api.service';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { SlutredovisningStep0FormData } from './slutredovisning-form-step0/slutredovisning-form-step0.component';
|
||||
import { SlutredovisningStep1FormData } from './slutredovisning-form-step1/slutredovisning-form-step1.component';
|
||||
import { SlutredovisningStep } from './slutredovisning-form.model';
|
||||
import { SlutredovisningStep2FormData } from './slutredovisning-form-step2/slutredovisning-form-step2.component';
|
||||
import { SlutredovisningRequest } from '@msfa-models/api/slutredovisning.request.model';
|
||||
|
||||
@Injectable()
|
||||
export class SlutredovisningFormService {
|
||||
private _currentStep$ = new BehaviorSubject<SlutredovisningStep>('step0');
|
||||
currentStep$ = this._currentStep$.asObservable();
|
||||
|
||||
private _step0FormData$ = new BehaviorSubject<SlutredovisningStep0FormData | null>(null);
|
||||
step0FormData$ = this._step0FormData$.asObservable();
|
||||
private _step1FormData$ = new BehaviorSubject<SlutredovisningStep1FormData | null>(null);
|
||||
step1FormData$ = this._step1FormData$.asObservable();
|
||||
|
||||
private _step2FormData$ = new BehaviorSubject<SlutredovisningStep2FormData | null>(null);
|
||||
|
||||
yrkesomraden$: Observable<Yrkesomrade[]> = this.slutredovisningApiService.fetchYrken$().pipe(
|
||||
map(({ data }) => data.map(yo => mapResponseToYrkesomrade(yo))),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
yrkeToTextMap$: Observable<{ [key: string]: string }> = this.yrkesomraden$.pipe(
|
||||
map(yrkesomraden => yrkeToTextMap(yrkesomraden))
|
||||
);
|
||||
|
||||
step2FormData$ = this._step2FormData$.asObservable();
|
||||
|
||||
constructor(
|
||||
private slutredovisningApiService: SlutredovisningApiService,
|
||||
private deltagareApiService: DeltagareApiService,
|
||||
private gemensamPlaneringApiService: GemensamPlaneringApiService
|
||||
) {}
|
||||
setStep(step: SlutredovisningStep) {
|
||||
this._currentStep$.next(step);
|
||||
}
|
||||
|
||||
submitSlutredovisning$(slutredovisning: SlutredovisningRequest): Observable<unknown> {
|
||||
return this.slutredovisningApiService.submitSlutredovisning$(slutredovisning);
|
||||
}
|
||||
|
||||
fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
|
||||
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
|
||||
}
|
||||
|
||||
processStep0Data(formData: SlutredovisningStep0FormData) {
|
||||
this._step0FormData$.next(formData);
|
||||
this.setStep('step1');
|
||||
}
|
||||
|
||||
processStep1Data(formData: SlutredovisningStep1FormData) {
|
||||
this._step1FormData$.next(formData);
|
||||
this.setStep('step2');
|
||||
}
|
||||
|
||||
processStep2Data(formData: SlutredovisningStep2FormData) {
|
||||
this._step2FormData$.next(formData);
|
||||
this.setStep('step3');
|
||||
}
|
||||
|
||||
getAllChosenActivities(genomforandeReferens: number): Observable<Activity[]> {
|
||||
return this.gemensamPlaneringApiService
|
||||
.fetchAllChosenActivities(genomforandeReferens)
|
||||
.pipe(map(({ data }) => data.map(activity => mapResponseToActivity(activity))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { SlutredovisningStep0FormData } from '../slutredovisning-form-step0/slutredovisning-form-step0.component';
|
||||
import { SlutredovisningStep1FormData } from '../slutredovisning-form-step1/slutredovisning-form-step1.component';
|
||||
import { SlutredovisningStep2FormData } from '../slutredovisning-form-step2/slutredovisning-form-step2.component';
|
||||
|
||||
import {
|
||||
Anstallningsform,
|
||||
MainOccupation,
|
||||
Omfattning,
|
||||
StillUnemployedReason,
|
||||
} from '@msfa-models/slutredovisning.model';
|
||||
import {
|
||||
SlutredovisningRequest,
|
||||
SlutredovisningRequestMainOccupationDetails,
|
||||
} from '@msfa-models/api/slutredovisning.request.model';
|
||||
import { SlutredovisningFormData } from '../models/slutredovisning-form-data.model';
|
||||
|
||||
export function formsToSlutredovisningFormData(
|
||||
genomforandereferens: number,
|
||||
step0FormData: SlutredovisningStep0FormData,
|
||||
step1FormData: SlutredovisningStep1FormData,
|
||||
step2FormData: SlutredovisningStep2FormData
|
||||
): SlutredovisningFormData {
|
||||
if (!genomforandereferens || !step0FormData || !step1FormData || !step2FormData) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mainOccupation: SlutredovisningRequestMainOccupationDetails;
|
||||
if (step0FormData.mainOccupation === MainOccupation.Other) {
|
||||
mainOccupation = {
|
||||
type: MainOccupation.Other,
|
||||
other: {
|
||||
otherExplanation: step0FormData.other.otherExplanation,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (step0FormData.mainOccupation === MainOccupation.Work) {
|
||||
mainOccupation = {
|
||||
type: MainOccupation.Work,
|
||||
work: step0FormData.work.map(workItem => {
|
||||
return {
|
||||
yrkesomrade: workItem.yrkesomrade,
|
||||
yrkesgrupp: workItem.yrkesgrupp,
|
||||
omfattning: workItem.omfattning as Omfattning,
|
||||
omfattningPercent: workItem.omfattningPercent,
|
||||
otherExplanation: workItem.otherExplanation,
|
||||
anstallningsform: workItem.anstallningsform as Anstallningsform,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
if (step0FormData.mainOccupation === MainOccupation.Education) {
|
||||
mainOccupation = {
|
||||
type: MainOccupation.Education,
|
||||
education: {
|
||||
educationLength: step0FormData.education.educationLength,
|
||||
educationLevel: step0FormData.education.educationLevel,
|
||||
otherExplanation: step0FormData.education.otherExplanation,
|
||||
educationSpecification: step0FormData.education.educationSpecification,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (step0FormData.mainOccupation === MainOccupation.StillUnemployed) {
|
||||
mainOccupation = {
|
||||
type: MainOccupation.StillUnemployed,
|
||||
[MainOccupation.StillUnemployed]: {
|
||||
reasonsGoalNotReached: Object.entries(step0FormData.stillUnemployed.reasonsGoalNotReached)
|
||||
.map(([reason, isChecked]) => ({ reason: reason as StillUnemployedReason, isChecked }))
|
||||
.filter(reasonChecked => !!reasonChecked.isChecked)
|
||||
.map(({ reason }) => reason),
|
||||
otherExplanation: step0FormData.stillUnemployed.stillUnemployedExplanation,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
genomforandereferens: genomforandereferens,
|
||||
mainOccupation: mainOccupation,
|
||||
activities: step1FormData.activities,
|
||||
progressDescription: step2FormData.progressDescription,
|
||||
nextStepDescription: step2FormData.nextStepDescription,
|
||||
otherInformation: step2FormData.otherInformation,
|
||||
};
|
||||
}
|
||||
|
||||
export function slutredovisningFormDataToSlutredovisningRequest(
|
||||
slutredovisningResponse: SlutredovisningFormData
|
||||
): SlutredovisningRequest {
|
||||
return {
|
||||
...slutredovisningResponse,
|
||||
activities: slutredovisningResponse.activities.map(({ whatHasBeenDone, id }) => ({ whatHasBeenDone, id })),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<msfa-layout>
|
||||
<msfa-report-layout
|
||||
*ngIf="avrop$ | async as avrop; else skeletonRef"
|
||||
reportTitle="Gemensam planering"
|
||||
[avrop]="avrop"
|
||||
>
|
||||
<msfa-slutredovisning-view-description-list
|
||||
[slutredovisning]="slutredovisning$ | async"
|
||||
></msfa-slutredovisning-view-description-list>
|
||||
</msfa-report-layout>
|
||||
</msfa-layout>
|
||||
|
||||
<ng-template #skeletonRef>
|
||||
<ui-skeleton [uiCount]="3" uiText="Laddar Gemensam planering"></ui-skeleton>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #loadingRef>
|
||||
<ui-loader uiType="padded"></ui-loader>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,31 @@
|
||||
@import 'mixins/list';
|
||||
@import 'variables/gutters';
|
||||
|
||||
.gemensam-planering-view {
|
||||
max-width: var(--digi--typography--text--max-width);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--l;
|
||||
|
||||
&__activity-list {
|
||||
@include msfa__reset-list;
|
||||
margin-bottom: var(--digi--layout--gutter--s);
|
||||
}
|
||||
|
||||
&__activity-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--digi--layout--gutter--s);
|
||||
margin-top: var(--digi--layout--gutter--s);
|
||||
}
|
||||
|
||||
&__activity-check {
|
||||
color: var(--digi--ui--color--border--success);
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--digi--layout--gutter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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 { SlutredovisningViewComponent } from './slutredovisning-view.component';
|
||||
import { SlutredovisningViewService } from './slutredovisning-view.service';
|
||||
|
||||
describe('GemensamPlaneringViewComponent', () => {
|
||||
let component: SlutredovisningViewComponent;
|
||||
let fixture: ComponentFixture<SlutredovisningViewComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [SlutredovisningViewComponent, LayoutComponent],
|
||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
||||
providers: [SlutredovisningViewService],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SlutredovisningViewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Params } from '@msfa-models/api/params.model';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, shareReplay, switchMap } from 'rxjs/operators';
|
||||
import { SlutredovisningViewService } from './slutredovisning-view.service';
|
||||
import { Slutredovisning } from '@msfa-models/slutredovisning.model';
|
||||
|
||||
@Component({
|
||||
selector: 'msfa-slutredovisning-view',
|
||||
templateUrl: './slutredovisning-view.component.html',
|
||||
styleUrls: ['./slutredovisning-view.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SlutredovisningViewComponent {
|
||||
params$: Observable<Params> = this.activatedRoute.params.pipe(
|
||||
map(params => ({
|
||||
handlingId: params.handlingId as string,
|
||||
genomforandeReferens: params.genomforandeReferens as string,
|
||||
}))
|
||||
);
|
||||
avrop$: Observable<Avrop> = this.params$.pipe(
|
||||
switchMap(({ genomforandeReferens }) =>
|
||||
this.slutredovisningViewService.fetchAvropInformation$(+genomforandeReferens)
|
||||
),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
slutredovisning$: Observable<Slutredovisning> = this.params$.pipe(
|
||||
switchMap(({ handlingId }) => this.slutredovisningViewService.fetchSlutredovisning$(handlingId as string)),
|
||||
shareReplay(1)
|
||||
);
|
||||
|
||||
constructor(private slutredovisningViewService: SlutredovisningViewService, private activatedRoute: ActivatedRoute) {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||
import { UiLoaderModule } from '@ui/loader/loader.module';
|
||||
import { UiSkeletonModule } from '@ui/skeleton/skeleton.module';
|
||||
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
|
||||
import { SlutredovisningViewComponent } from './slutredovisning-view.component';
|
||||
import { SlutredovisningViewService } from './slutredovisning-view.service';
|
||||
import { SlutredovisningViewDescriptionListModule } from '../../../components/slutredovisning-view-description-list/slutredovisning-view-description-list.module';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [SlutredovisningViewComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RouterModule.forChild([{ path: '', component: SlutredovisningViewComponent }]),
|
||||
LayoutModule,
|
||||
ReportLayoutModule,
|
||||
BackLinkModule,
|
||||
UiLoaderModule,
|
||||
UiSkeletonModule,
|
||||
SlutredovisningViewDescriptionListModule,
|
||||
],
|
||||
providers: [SlutredovisningViewService],
|
||||
exports: [SlutredovisningViewComponent],
|
||||
})
|
||||
export class SlutredovisningViewModule {}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Avrop } from '@msfa-models/avrop.model';
|
||||
import { HandlingarApiService } from '@msfa-services/api/handlingar.api.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||
import { SlutredovisningApiService } from '@msfa-services/api/slutredovisning.api.service';
|
||||
import { SlutredovisningResponse } from '@msfa-models/api/slutredovisning.response.model';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class SlutredovisningViewService {
|
||||
constructor(
|
||||
private handlingarApiService: HandlingarApiService,
|
||||
private deltagareApiService: DeltagareApiService,
|
||||
private gemensamPlaneringApiService: SlutredovisningApiService
|
||||
) {}
|
||||
|
||||
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
|
||||
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
|
||||
}
|
||||
|
||||
public fetchSlutredovisning$(handlingId: string): Observable<SlutredovisningResponse> {
|
||||
return this.handlingarApiService.fetchSlutredovisning$(handlingId).pipe(map(({ data }) => data));
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,14 @@
|
||||
<div class="error-list-wrapper" [hidden]="!validationErrorLinks || validationErrorLinks.length === 0">
|
||||
<digi-notification-alert
|
||||
class="error-list"
|
||||
af-variation="danger"
|
||||
[attr.af-heading]="headingText"
|
||||
[af-heading-level]="headingLevel"
|
||||
[afCloseable]="false"
|
||||
<digi-form-error-list
|
||||
*ngIf="validationErrorLinks && validationErrorLinks.length"
|
||||
[afHeading]="headingText"
|
||||
[afOverrideLink]="true"
|
||||
(afOnClickLink)="setFocusOnFormElement($event.detail.detail.target)"
|
||||
>
|
||||
<a
|
||||
msfaAnchorLink
|
||||
*ngFor="let validationErrorLink of validationErrorLinks"
|
||||
[attr.href]="'#' + validationErrorLink.elementId"
|
||||
>
|
||||
<ul class="error-list__validation-error-links">
|
||||
<li *ngFor="let validationErrorLink of validationErrorLinks;">
|
||||
<digi-ng-link-internal
|
||||
msfaAnchorLink
|
||||
class="error-list__validation-error-link"
|
||||
[afHref]="'#' + validationErrorLink.elementId"
|
||||
[afText]="validationErrorLink.text"
|
||||
></digi-ng-link-internal>
|
||||
</li>
|
||||
</ul>
|
||||
</digi-notification-alert>
|
||||
</div>
|
||||
<!-- <digi-form-error-list
|
||||
class="edit-employee-form__validation-error-summary"
|
||||
af-heading="Åtgärda följande fel för att spara dina ändringar:"
|
||||
*ngIf="validationErrorLinks && validationErrorLinks.length !== 0"
|
||||
>
|
||||
Behöver hantera ankarlänkar kopplat till den här komponenten om det ska fungera att använda den...
|
||||
<a
|
||||
[attr.href]="'#' + validationErrorLink.elementId"
|
||||
[attr.id]="first ? firstValidationErrorLinkId : 'validation-error-link-' + index"
|
||||
*ngFor="let validationErrorLink of validationErrorLinks; let first = first; let index = index"
|
||||
>
|
||||
{{ validationErrorLink.text }}
|
||||
</a>
|
||||
</digi-form-error-list> -->
|
||||
{{ validationErrorLink.text }}
|
||||
</a>
|
||||
</digi-form-error-list>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TypographyDynamicHeadingLevel } from '@af/digi-ng/_typography/typography-dynamic-heading';
|
||||
import { Component, ChangeDetectionStrategy, Input } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
|
||||
export interface ValidationErrorLink {
|
||||
elementId: string;
|
||||
@@ -16,4 +16,22 @@ export class ErrorListComponent {
|
||||
@Input() validationErrorLinks: ValidationErrorLink[] = [];
|
||||
@Input() headingText: string;
|
||||
@Input() headingLevel: TypographyDynamicHeadingLevel = TypographyDynamicHeadingLevel.H3;
|
||||
|
||||
setFocusOnFormElement(element: HTMLElement): void {
|
||||
const id = element.getAttribute('href').replace('#', '');
|
||||
const target = document.getElementById(id);
|
||||
|
||||
if (target) {
|
||||
const targetPosition: DOMRect = target.getBoundingClientRect();
|
||||
const absoluteElementPosition = targetPosition.top + window.scrollY;
|
||||
const newPosition = absoluteElementPosition - window.innerHeight / 2;
|
||||
|
||||
if (target.focus) {
|
||||
target.tabIndex = -1;
|
||||
target.focus();
|
||||
window.scrollTo(0, newPosition);
|
||||
target.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ export const DELTAGARE_REPORTING_ROUTES = {
|
||||
avvikelserapport: 'Avvikelserapport (avvikelse)',
|
||||
signal: 'Signal om arbete eller studier',
|
||||
'informativ-rapport': 'Informativ rapport',
|
||||
slutredovisning: 'Slutredovisning',
|
||||
};
|
||||
|
||||
export const NAVIGATION = {
|
||||
|
||||
2130
apps/mina-sidor-fa/src/app/shared/constants/yrken.ts
Normal file
2130
apps/mina-sidor-fa/src/app/shared/constants/yrken.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,13 +12,17 @@ export class AnchorLinkDirective {
|
||||
const elementId = href?.trim().replace('#', '');
|
||||
const element = document.getElementById(elementId);
|
||||
|
||||
if (element && element.focus) {
|
||||
element.tabIndex = -1;
|
||||
element.focus();
|
||||
}
|
||||
if (element) {
|
||||
const elementPosition: DOMRect = element.getBoundingClientRect();
|
||||
const absoluteElementPosition = elementPosition.top + window.scrollY;
|
||||
const newPosition = absoluteElementPosition - window.innerHeight / 2;
|
||||
|
||||
if (element.tabIndex < 0) {
|
||||
element.scrollIntoView();
|
||||
if (element.focus) {
|
||||
element.tabIndex = -1;
|
||||
element.focus();
|
||||
window.scrollTo(0, newPosition);
|
||||
element.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -16,5 +16,6 @@ export enum Feature {
|
||||
REPORTING_PERIODISK_REDOVISNING,
|
||||
REPORTING_INFORMATIV_RAPPORT,
|
||||
EXPORTS,
|
||||
SLUTREDOVISNING,
|
||||
NEWS,
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum ReportType {
|
||||
Avvikelse = 'Avvikelserapport (avvikelse)',
|
||||
Franvaro = 'Avvikelserapport (frånvaro)',
|
||||
PeriodiskRedovisning = 'Periodisk redovisning',
|
||||
Slutredovisning = 'Slutredovisning',
|
||||
InformativRapport = 'Informativ rapport',
|
||||
InformativRedovisning = 'Informativ rapport',
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
Anstallningsform,
|
||||
EducationLength,
|
||||
EducationLevel,
|
||||
MainOccupation,
|
||||
Omfattning,
|
||||
StillUnemployedReason,
|
||||
} from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationWorkDetails {
|
||||
yrkesomrade: string;
|
||||
yrkesgrupp: string;
|
||||
anstallningsform: Anstallningsform;
|
||||
otherExplanation: string;
|
||||
omfattning: Omfattning;
|
||||
omfattningPercent: number;
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationWork {
|
||||
type: MainOccupation.Work;
|
||||
work: SlutredovisningRequestMainOccupationWorkDetails[];
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationEducationDetails {
|
||||
educationLevel: EducationLevel;
|
||||
otherExplanation: string;
|
||||
educationLength: EducationLength;
|
||||
educationSpecification: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationEducation {
|
||||
type: MainOccupation.Education;
|
||||
education: SlutredovisningRequestMainOccupationEducationDetails;
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationOtherDetails {
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationOther {
|
||||
type: MainOccupation.Other;
|
||||
other: SlutredovisningRequestMainOccupationOtherDetails;
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationStillUnemployedDetails {
|
||||
reasonsGoalNotReached: StillUnemployedReason[];
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningRequestMainOccupationStillUnemployed {
|
||||
type: MainOccupation.StillUnemployed;
|
||||
stillUnemployed: SlutredovisningRequestMainOccupationStillUnemployedDetails;
|
||||
}
|
||||
|
||||
export type SlutredovisningRequestMainOccupationDetails =
|
||||
| SlutredovisningRequestMainOccupationWork
|
||||
| SlutredovisningRequestMainOccupationEducation
|
||||
| SlutredovisningRequestMainOccupationOther
|
||||
| SlutredovisningRequestMainOccupationStillUnemployed;
|
||||
|
||||
export interface SlutredovisningRequest {
|
||||
genomforandereferens: number;
|
||||
mainOccupation: SlutredovisningRequestMainOccupationDetails;
|
||||
activities: { id: string; whatHasBeenDone: string }[];
|
||||
progressDescription: string;
|
||||
nextStepDescription: string;
|
||||
otherInformation: string;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
Anstallningsform,
|
||||
EducationLength,
|
||||
EducationLevel,
|
||||
MainOccupation,
|
||||
Omfattning,
|
||||
StillUnemployedReason,
|
||||
} from '@msfa-models/slutredovisning.model';
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationWorkDetails {
|
||||
yrkesomrade: string;
|
||||
yrkesgrupp: string;
|
||||
yrkesomradeName: string;
|
||||
yrkesgruppName: string;
|
||||
anstallningsform: Anstallningsform;
|
||||
otherExplanation: string;
|
||||
omfattning: Omfattning;
|
||||
omfattningPercent: number;
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationWork {
|
||||
type: MainOccupation.Work;
|
||||
work: SlutredovisningResponseMainOccupationWorkDetails[];
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationEducationDetails {
|
||||
educationLevel: EducationLevel;
|
||||
otherExplanation: string;
|
||||
educationLength: EducationLength;
|
||||
educationSpecification: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationEducation {
|
||||
type: MainOccupation.Education;
|
||||
education: SlutredovisningResponseMainOccupationEducationDetails;
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationOtherDetails {
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationOther {
|
||||
type: MainOccupation.Other;
|
||||
other: SlutredovisningResponseMainOccupationOtherDetails;
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationStillUnemployedDetails {
|
||||
reasonsGoalNotReached: StillUnemployedReason[];
|
||||
otherExplanation: string;
|
||||
}
|
||||
|
||||
export interface SlutredovisningResponseMainOccupationStillUnemployed {
|
||||
type: MainOccupation.StillUnemployed;
|
||||
stillUnemployed: SlutredovisningResponseMainOccupationStillUnemployedDetails;
|
||||
}
|
||||
|
||||
export type SlutredovisningResponseMainOccupationDetails =
|
||||
| SlutredovisningResponseMainOccupationWork
|
||||
| SlutredovisningResponseMainOccupationEducation
|
||||
| SlutredovisningResponseMainOccupationOther
|
||||
| SlutredovisningResponseMainOccupationStillUnemployed;
|
||||
|
||||
export interface SlutredovisningResponse {
|
||||
genomforandereferens: number;
|
||||
mainOccupation: SlutredovisningResponseMainOccupationDetails;
|
||||
activities: { id: string; whatHasBeenDone: string; name: string }[];
|
||||
progressDescription: string;
|
||||
nextStepDescription: string;
|
||||
otherInformation: string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface YrkesgruppResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
parentId: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { YrkesgruppResponse } from './yrkesgrupp.response.model';
|
||||
|
||||
export interface YrkesomradeResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
items: YrkesgruppResponse[];
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
import { SlutredovisningResponse } from '@msfa-models/api/slutredovisning.response.model';
|
||||
|
||||
export enum MainOccupation {
|
||||
Work = 'work',
|
||||
Education = 'education',
|
||||
StillUnemployed = 'stillUnemployed',
|
||||
ByteTillNyLeverantorIRustaOchMatcha = 'byte till ny leverantör i rusta och matcha',
|
||||
Other = 'other',
|
||||
}
|
||||
|
||||
export enum EducationLevel {
|
||||
HogskolaEllerUniversitet = 'högskola eller universitet',
|
||||
Yrkeshogskola = 'yrkeshögskola',
|
||||
KomvuxGymnasiumFolkhogskola = 'komvux, gymnasium eller folkhögskola',
|
||||
VetEj = 'vet ej',
|
||||
Annat = 'annat',
|
||||
}
|
||||
|
||||
export function educationLevelToString(educationLevel: EducationLevel): string | null {
|
||||
switch (educationLevel) {
|
||||
case EducationLevel.HogskolaEllerUniversitet:
|
||||
return 'Högskola eller universitet';
|
||||
case EducationLevel.Yrkeshogskola:
|
||||
return 'Yrkeshögskola';
|
||||
case EducationLevel.KomvuxGymnasiumFolkhogskola:
|
||||
return 'Komvux, gymnasium eller folkhögskola';
|
||||
case EducationLevel.VetEj:
|
||||
return 'Vet ej';
|
||||
case EducationLevel.Annat:
|
||||
return 'Annat';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export enum StillUnemployedReason {
|
||||
SaknarRelevantUtbildning = 'saknar relevant utbildning',
|
||||
SaknarArbetslivserfarenhet = 'saknar arbetslivserfarenhet',
|
||||
SaknarNatverkOchKontakter = 'saknar nätverk och kontakter',
|
||||
BristandeSprakkunskaperISvenska = 'bristande språkkunskaper i svenska',
|
||||
KonjukturLaget = 'konjunkturläget',
|
||||
Annat = 'annat',
|
||||
}
|
||||
|
||||
export enum EducationLength {
|
||||
UpToOneYear = 'upp till ett år',
|
||||
OneToTwoYears = 'ett år till två år',
|
||||
OverTwoYears = 'två år eller längre',
|
||||
}
|
||||
|
||||
export function educationLengthToString(educationLength: EducationLength): string | null {
|
||||
switch (educationLength) {
|
||||
case EducationLength.UpToOneYear:
|
||||
return 'Upp till ett år';
|
||||
case EducationLength.OneToTwoYears:
|
||||
return 'Ett år till två år';
|
||||
case EducationLength.OverTwoYears:
|
||||
return 'Två år eller längre';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export enum Anstallningsform {
|
||||
Tillsvidare = 'tillsvidareanställning',
|
||||
Prov = 'provanstallning',
|
||||
Visstid = 'visstidsanställning',
|
||||
VetEj = 'vet ej',
|
||||
Annat = 'annat',
|
||||
}
|
||||
|
||||
export enum Omfattning {
|
||||
Heltid = 'heltid',
|
||||
Deltid = 'deltid',
|
||||
VetEj = 'vet ej',
|
||||
}
|
||||
|
||||
export function omfattningToString(omfattning: Omfattning): string | null {
|
||||
switch (omfattning) {
|
||||
case Omfattning.Heltid:
|
||||
return 'Heltid';
|
||||
case Omfattning.Deltid:
|
||||
return 'Deltid';
|
||||
case Omfattning.VetEj:
|
||||
return 'Vet ej';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export type Slutredovisning = SlutredovisningResponse;
|
||||
|
||||
export function mainOccupationToString(mainOccupation: MainOccupation): string | null {
|
||||
switch (mainOccupation) {
|
||||
case MainOccupation.Education:
|
||||
return 'Utbildning';
|
||||
case MainOccupation.StillUnemployed:
|
||||
return 'Fortsatt arbetssökande';
|
||||
case MainOccupation.ByteTillNyLeverantorIRustaOchMatcha:
|
||||
return 'Byte till ny leverantör i Rusta och matcha';
|
||||
case MainOccupation.Other:
|
||||
return 'Annat';
|
||||
case MainOccupation.Work:
|
||||
return 'Arbete';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
13
apps/mina-sidor-fa/src/app/shared/models/yrkesgrupp.model.ts
Normal file
13
apps/mina-sidor-fa/src/app/shared/models/yrkesgrupp.model.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { YrkesgruppResponse } from './api/yrkesgrupp.response.model';
|
||||
|
||||
export interface Yrkesgrupp {
|
||||
value: string;
|
||||
name: string;
|
||||
parentId: string;
|
||||
}
|
||||
|
||||
export function mapResponseToYrkesgrupp(data: YrkesgruppResponse): Yrkesgrupp {
|
||||
const { id, name, parentId } = data;
|
||||
|
||||
return { value: id, name, parentId };
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { YrkesomradeResponse } from './api/yrkesomrade.response.model';
|
||||
import { mapResponseToYrkesgrupp, Yrkesgrupp } from './yrkesgrupp.model';
|
||||
|
||||
export interface Yrkesomrade {
|
||||
value: string;
|
||||
name: string;
|
||||
items: Yrkesgrupp[];
|
||||
}
|
||||
|
||||
export function mapResponseToYrkesomrade(data: YrkesomradeResponse): Yrkesomrade {
|
||||
const { id, name, items } = data;
|
||||
|
||||
return { value: id, name, items: items.map(item => mapResponseToYrkesgrupp(item)) };
|
||||
}
|
||||
|
||||
export function yrkeToTextMap(yrkesomraden: Yrkesomrade[]): { [key: string]: string } {
|
||||
const translateMap: { [key: string]: string } = {};
|
||||
|
||||
yrkesomraden.forEach(yrkesomrade => {
|
||||
translateMap[yrkesomrade.value] = yrkesomrade.name;
|
||||
yrkesomrade.items.forEach(yrkesgrupp => {
|
||||
translateMap[yrkesgrupp.value] = yrkesgrupp.name;
|
||||
});
|
||||
});
|
||||
return translateMap;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { FranvaroReportResponse } from '@msfa-models/api/franvaro-response.model
|
||||
import { GemensamPlaneringResponse } from '@msfa-models/api/gemensam-planering.response.model';
|
||||
import { InformativRapportResponse } from '@msfa-models/api/informativ-rapport.response.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { SlutredovisningResponse } from '@msfa-models/api/slutredovisning.response.model';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -34,4 +35,8 @@ export class HandlingarApiService {
|
||||
public fetchAvvikelseReport$(handlingId: string): Observable<{ data: AvvikelseReportResponse }> {
|
||||
return this.httpClient.get<{ data: AvvikelseReportResponse }>(`${this._apiBaseUrl}/avvikelse/${handlingId}`);
|
||||
}
|
||||
|
||||
public fetchSlutredovisning$(handlingId: string) {
|
||||
return this.httpClient.get<{ data: SlutredovisningResponse }>(`${this._apiBaseUrl}/slutredovisning/${handlingId}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { YRKEN } from '@msfa-constants/yrken';
|
||||
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||
import { environment } from '@msfa-environment';
|
||||
import { SlutredovisningRequest } from '@msfa-models/api/slutredovisning.request.model';
|
||||
import { SlutredovisningResponse } from '@msfa-models/api/slutredovisning.response.model';
|
||||
import { YrkesomradeResponse } from '@msfa-models/api/yrkesomrade.response.model';
|
||||
import { CustomError } from '@msfa-models/error/custom-error';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SlutredovisningApiService {
|
||||
private _apiBaseUrl = `${environment.api.url}/rapporter/slutredovisning`;
|
||||
private _handlingarBaseUrl = `${environment.api.url}/handlingar`;
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
public fetchYrken$(): Observable<{ data: YrkesomradeResponse[] }> {
|
||||
return of({ data: YRKEN });
|
||||
}
|
||||
|
||||
public fetchSlutredovisning$(handlingId: string): Observable<{ data: SlutredovisningResponse }> {
|
||||
return this.httpClient.get<{ data: SlutredovisningResponse }>(
|
||||
`${this._handlingarBaseUrl}/slutredovisning/${handlingId}`
|
||||
);
|
||||
}
|
||||
|
||||
public submitSlutredovisning$(requestData: SlutredovisningRequest): Observable<void> {
|
||||
return this.httpClient.post<void>(`${this._apiBaseUrl}`, requestData).pipe(
|
||||
catchError((error: Error) => {
|
||||
throw new CustomError({
|
||||
error,
|
||||
message: `Kunde inte spara Periodisk redovisning.\n\n${error.message}`,
|
||||
type: ErrorType.API,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function capitalizeSentence(sentence: string): string {
|
||||
return sentence.charAt(0).toUpperCase() + sentence.slice(1);
|
||||
}
|
||||
@@ -28,6 +28,7 @@ export const ACTIVE_FEATURES_TEST: Feature[] = [
|
||||
Feature.VERSION_INFO,
|
||||
Feature.REPORTING_PERIODISK_REDOVISNING,
|
||||
Feature.REPORTING_INFORMATIV_RAPPORT,
|
||||
Feature.SLUTREDOVISNING,
|
||||
Feature.EXPORTS,
|
||||
Feature.NEWS,
|
||||
];
|
||||
|
||||
4
libs/ui/src/error-list/error-link.model.ts
Normal file
4
libs/ui/src/error-list/error-link.model.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface ErrorLink {
|
||||
elementId: string;
|
||||
text: string;
|
||||
}
|
||||
11
libs/ui/src/error-list/error-list.component.html
Normal file
11
libs/ui/src/error-list/error-list.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<digi-form-error-list
|
||||
*ngIf="uiErrorLinks && uiErrorLinks.length"
|
||||
[afHeading]="uiHeadingText"
|
||||
[afDescription]="uiDescription"
|
||||
[afOverrideLink]="true"
|
||||
(afOnClickLink)="setFocusOnFormElement($event.detail.detail.target)"
|
||||
>
|
||||
<a *ngFor="let validationErrorLink of uiErrorLinks" [attr.href]="'#' + validationErrorLink.elementId">
|
||||
{{ validationErrorLink.text }}
|
||||
</a>
|
||||
</digi-form-error-list>
|
||||
0
libs/ui/src/error-list/error-list.component.scss
Normal file
0
libs/ui/src/error-list/error-list.component.scss
Normal file
25
libs/ui/src/error-list/error-list.component.spec.ts
Normal file
25
libs/ui/src/error-list/error-list.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ErrorListComponent } from './error-list.component';
|
||||
|
||||
describe('ErrorListComponent', () => {
|
||||
let component: ErrorListComponent;
|
||||
let fixture: ComponentFixture<ErrorListComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [ErrorListComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ErrorListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
33
libs/ui/src/error-list/error-list.component.ts
Normal file
33
libs/ui/src/error-list/error-list.component.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { ErrorLink } from './error-link.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-error-list',
|
||||
templateUrl: './error-list.component.html',
|
||||
styleUrls: ['./error-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ErrorListComponent {
|
||||
@Input() uiErrorLinks: ErrorLink[] = [];
|
||||
@Input() uiHeadingText: string;
|
||||
@Input() uiDescription: string;
|
||||
|
||||
setFocusOnFormElement(element: HTMLElement): void {
|
||||
const id = element.getAttribute('href').replace('#', '');
|
||||
const target = document.getElementById(id);
|
||||
|
||||
if (target) {
|
||||
const targetPosition: DOMRect = target.getBoundingClientRect();
|
||||
const absoluteElementPosition = targetPosition.top + window.scrollY;
|
||||
const newPosition = absoluteElementPosition - window.innerHeight / 2;
|
||||
|
||||
if (target.focus) {
|
||||
console.log(target);
|
||||
target.tabIndex = -1;
|
||||
target.focus();
|
||||
window.scrollTo(0, newPosition);
|
||||
target.tabIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
libs/ui/src/error-list/error-list.module.ts
Normal file
11
libs/ui/src/error-list/error-list.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { ErrorListComponent } from './error-list.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [ErrorListComponent],
|
||||
imports: [CommonModule],
|
||||
exports: [ErrorListComponent],
|
||||
})
|
||||
export class UiErrorListModule {}
|
||||
16
libs/ui/src/input/input-type.enum.ts
Normal file
16
libs/ui/src/input/input-type.enum.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export enum UiInputType {
|
||||
COLOR = 'color',
|
||||
DATE = 'date',
|
||||
DATETIME_LOCAL = 'datetime-local',
|
||||
EMAIL = 'email',
|
||||
HIDDEN = 'hidden',
|
||||
MONTH = 'month',
|
||||
NUMBER = 'number',
|
||||
PASSWORD = 'password',
|
||||
SEARCH = 'search',
|
||||
TEL = 'tel',
|
||||
TEXT = 'text',
|
||||
TIME = 'time',
|
||||
URL = 'url',
|
||||
WEEK = 'week',
|
||||
}
|
||||
5
libs/ui/src/input/input-variation.enum.ts
Normal file
5
libs/ui/src/input/input-variation.enum.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum UiInputVariation {
|
||||
S = 's',
|
||||
M = 'm',
|
||||
L = 'l',
|
||||
}
|
||||
23
libs/ui/src/input/input.component.html
Normal file
23
libs/ui/src/input/input.component.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<div class="ui-input" [ngClass]="{'ui-input--invalid': uiInvalid && uiValidationMessage}">
|
||||
<digi-form-input
|
||||
class="ui-input__input"
|
||||
[afId]="uiId"
|
||||
[afName]="uiName"
|
||||
[afLabel]="uiLabel"
|
||||
[afLabelDescription]="uiDescription"
|
||||
[afType]="uiType"
|
||||
[afVariation]="uiVariation"
|
||||
[afRequired]="uiRequired"
|
||||
[afAnnounceIfOptional]="uiAnnounceIfOptional"
|
||||
[afMin]="uiMin"
|
||||
[afMax]="uiMax"
|
||||
[afValue]="currentValue"
|
||||
[afValidation]="uiInvalid ? 'error' : 'neutral'"
|
||||
(afOnInput)="checkForChange($event.detail.target.value)"
|
||||
></digi-form-input>
|
||||
<div class="ui-input__validation" aria-atomic="true" role="alert">
|
||||
<digi-form-validation-message *ngIf="uiInvalid && uiValidationMessage" af-variation="error"
|
||||
>{{uiValidationMessage}}</digi-form-validation-message
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
8
libs/ui/src/input/input.component.scss
Normal file
8
libs/ui/src/input/input.component.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
.ui-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&--invalid {
|
||||
gap: var(--digi--layout--gutter--s);
|
||||
}
|
||||
}
|
||||
23
libs/ui/src/input/input.component.spec.ts
Normal file
23
libs/ui/src/input/input.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import { InputComponent } from './input.component';
|
||||
|
||||
export class MockInjector {
|
||||
get = jest.fn();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: max-classes-per-file
|
||||
export class MockChangeDetectorRef {
|
||||
markForCheck = jest.fn();
|
||||
detach = jest.fn();
|
||||
detectChanges = jest.fn();
|
||||
reattach = jest.fn();
|
||||
checkNoChanges = jest.fn();
|
||||
}
|
||||
|
||||
describe('InputComponent', () => {
|
||||
let component: InputComponent;
|
||||
it('should create', () => {
|
||||
component = new InputComponent(new MockInjector(), new MockChangeDetectorRef());
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
16
libs/ui/src/input/input.component.stories.ts
Normal file
16
libs/ui/src/input/input.component.stories.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { InputComponent } from './input.component';
|
||||
import { UiInputModule } from './input.module';
|
||||
|
||||
export default { title: 'Input', component: InputComponent };
|
||||
|
||||
const componentModule = {
|
||||
moduleMetadata: {
|
||||
imports: [ReactiveFormsModule, UiInputModule],
|
||||
},
|
||||
};
|
||||
|
||||
export const standard = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-input></ui-input>',
|
||||
});
|
||||
100
libs/ui/src/input/input.component.ts
Normal file
100
libs/ui/src/input/input.component.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Injector,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { uuid } from '@utils/uuid.util';
|
||||
import { UiInputType } from './input-type.enum';
|
||||
import { UiInputVariation } from './input-variation.enum';
|
||||
|
||||
/**
|
||||
* A input input. Implemented with control value accessor
|
||||
*
|
||||
* ## Usage
|
||||
* ``import {UiInputModule} from '@ui/input/input.module';``
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ui-input',
|
||||
templateUrl: './input.component.html',
|
||||
styleUrls: ['./input.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: InputComponent,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class InputComponent implements AfterViewInit, ControlValueAccessor, OnChanges {
|
||||
@Input() uiId: string = uuid();
|
||||
@Input() uiName: string;
|
||||
@Input() uiLabel: string = '';
|
||||
@Input() uiDescription: string;
|
||||
@Input() uiVariation: UiInputVariation = UiInputVariation.M;
|
||||
@Input() uiRequired: boolean;
|
||||
@Input() uiAnnounceIfOptional: boolean = false;
|
||||
@Input() uiMin: number;
|
||||
@Input() uiMax: number;
|
||||
@Input() uiType: UiInputType = UiInputType.TEXT;
|
||||
@Input() uiInvalid: boolean;
|
||||
@Input() uiValidationMessage: string;
|
||||
@Output() uiOnChange: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
name: string | number;
|
||||
|
||||
onTouched: () => {};
|
||||
private onChange: (value: any) => {};
|
||||
private _value: any;
|
||||
|
||||
constructor(private injector: Injector, private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
get currentValue(): string {
|
||||
return this._value || '';
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = this.uiName || ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = this.uiName || ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
checkForChange(rawValue: string): void {
|
||||
const value = this.uiType === UiInputType.NUMBER ? +rawValue : rawValue;
|
||||
if (this._value !== value) {
|
||||
if (this.onChange) {
|
||||
this.onChange(value);
|
||||
}
|
||||
this._value = value;
|
||||
this.uiOnChange.emit(value);
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._value = value;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => {}) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => {}) {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
}
|
||||
11
libs/ui/src/input/input.module.ts
Normal file
11
libs/ui/src/input/input.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { InputComponent } from './input.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [CommonModule],
|
||||
declarations: [InputComponent],
|
||||
exports: [InputComponent],
|
||||
})
|
||||
export class UiInputModule {}
|
||||
@@ -1,8 +1,9 @@
|
||||
<div class="ui-radiobutton-group" [ngClass]="{'ui-radiobutton-group--invalid': uiInvalid}">
|
||||
<div class="ui-radiobutton-group__radiobuttons" [ngClass]="radiobuttonsModifierClass">
|
||||
<digi-form-radiobutton
|
||||
*ngFor="let item of uiRadiobuttons"
|
||||
*ngFor="let item of uiRadiobuttons; let first = first; let index = index"
|
||||
class="ui-radiobutton-group__radiobutton"
|
||||
[afId]="first ? uiId : uiId + '-' + index"
|
||||
[afLabel]="getLabelText(item.label)"
|
||||
[afValue]="item.value"
|
||||
[afChecked]="currentValue === item.value"
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { uuid } from '@utils/uuid.util';
|
||||
import { RadiobuttonGroupDirection } from './radiobutton-group-direction.enum';
|
||||
import { Radiobutton } from './radiobutton.model';
|
||||
|
||||
@@ -41,6 +42,7 @@ export class RadiobuttonGroupComponent implements ControlValueAccessor, AfterVie
|
||||
@Input() uiSecondary: boolean;
|
||||
@Input() uiRequired: boolean;
|
||||
@Input() uiName: string;
|
||||
@Input() uiId: string = uuid();
|
||||
@Input() uiAnnounceIfOptional: boolean;
|
||||
@Output() uiOnChange = new EventEmitter<any>();
|
||||
|
||||
@@ -85,19 +87,20 @@ export class RadiobuttonGroupComponent implements ControlValueAccessor, AfterVie
|
||||
ngAfterViewInit(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name || this.uiName;
|
||||
this.name = this.uiName || ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name || this.uiName;
|
||||
this.name = this.uiName || ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
checkForChange(rawValue: any): void {
|
||||
const value = this._transformValue(rawValue);
|
||||
console.log(value);
|
||||
if (this._value !== value) {
|
||||
if (this.onChange) {
|
||||
this.onChange(value);
|
||||
|
||||
4
libs/ui/src/select/select-option.model.ts
Normal file
4
libs/ui/src/select/select-option.model.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export interface SelectOption {
|
||||
value: any;
|
||||
name: string;
|
||||
}
|
||||
45
libs/ui/src/select/select.component.html
Normal file
45
libs/ui/src/select/select.component.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<div class="ui-select" [ngClass]="{'ui-select--invalid': uiInvalid}">
|
||||
<div class="ui-select__input-wrapper">
|
||||
<digi-form-label
|
||||
[afLabel]="uiLabel"
|
||||
[afAnnounceIfOptional]="uiAnnounceIfOptional"
|
||||
[afRequired]="uiRequired"
|
||||
[afFor]="uiId"
|
||||
[afDescription]="uiDescription"
|
||||
></digi-form-label>
|
||||
<div class="ui-select__select-wrapper">
|
||||
<select
|
||||
class="ui-select__select"
|
||||
[ngClass]="{'ui-select__select--invalid': uiInvalid}"
|
||||
[attr.id]="uiId"
|
||||
[attr.name]="name"
|
||||
[attr.required]="uiRequired"
|
||||
(change)="checkForChange($event.target.value)"
|
||||
>
|
||||
<option
|
||||
*ngIf="uiPlaceholder"
|
||||
[selected]="!currentValue"
|
||||
class="ui-select__option ui-select__option--placeholder"
|
||||
disabled
|
||||
value=""
|
||||
>
|
||||
{{uiPlaceholder}}
|
||||
</option>
|
||||
<option
|
||||
*ngFor="let option of uiOptions"
|
||||
[selected]="option.value === currentValue"
|
||||
[attr.value]="option.value"
|
||||
class="ui-select__option"
|
||||
>
|
||||
{{option.name}}
|
||||
</option>
|
||||
</select>
|
||||
<digi-icon-arrow-down aria-hidden="true" class="ui-select__icon" slot="icon"></digi-icon-arrow-down>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-form-validation-message *ngIf="uiInvalid && uiValidationMessage" af-variation="error"
|
||||
>{{uiValidationMessage}}</digi-form-validation-message
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
45
libs/ui/src/select/select.component.scss
Normal file
45
libs/ui/src/select/select.component.scss
Normal file
@@ -0,0 +1,45 @@
|
||||
@import 'variables/gutters';
|
||||
@import 'functions/rem';
|
||||
|
||||
.ui-select {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&--invalid {
|
||||
gap: var(--digi--layout--gutter--xs);
|
||||
}
|
||||
|
||||
&__select-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__select {
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
padding: var(--digi--ui--input--padding);
|
||||
padding-right: var(--digi--layout--padding--30);
|
||||
height: var(--digi--ui--input--height);
|
||||
border-width: rem(1);
|
||||
cursor: pointer;
|
||||
font-size: var(--digi--typography--font-size--m);
|
||||
color: var(--digi--typography--color--text);
|
||||
background-color: var(--digi--ui--color--background);
|
||||
border-color: var(--digi--ui--input--border--color);
|
||||
|
||||
&--invalid {
|
||||
border-color: var(--digi--ui--color--border--error);
|
||||
background-color: var(--digi--ui--color--background--error);
|
||||
}
|
||||
}
|
||||
|
||||
&__option {
|
||||
font-size: var(--digi--typography--font-size--l);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: absolute;
|
||||
right: rem(12);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user