feat(reports): Added report views to view single reports. (TV-205)

Squashed commit of the following:

commit bb28714969a6ac2d2b57134179344169edc1dc1d
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Oct 12 14:15:45 2021 +0200

    Updated routing for signal

commit deb5082659065089c7014c7114538851e13fd6e4
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Oct 12 14:07:33 2021 +0200

    Moved around files

commit 2637261f21968e63fcab4b13cccd8d68ab2ac6fe
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Oct 12 11:22:11 2021 +0200

    Added avvikelse to single report page... also started with refactoring models a bit

commit 53c6a59551bfa0887b22d03419b3434a8c213b50
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Oct 12 09:56:22 2021 +0200

    Implemented frånvaro report

commit e8ccbf15885f0bfa6eb86a9681464a2e04457f99
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 11 16:42:16 2021 +0200

    Added gemensam planering page

commit ebbf6f43ca
Merge: 45aec375 eac45ebd
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 11 15:14:02 2021 +0200

    Merge branch 'next' into develop

commit 45aec3755f
Merge: e4cff086 99c0ac3a
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 11 13:24:11 2021 +0200

    Merge branch 'next' into develop

commit e4cff086c4
Merge: ef10270f 631418bc
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 11 09:47:05 2021 +0200

    Merge branch 'next' into develop

commit ef10270fa9
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 11 09:23:59 2021 +0200

    Removed test build from develop
This commit is contained in:
Erik Tiekstra
2021-10-12 14:23:27 +02:00
parent eac45ebd16
commit 0045cafd77
81 changed files with 1104 additions and 238 deletions

View File

@@ -12,17 +12,9 @@ const routes: Routes = [
loadChildren: () =>
import('./pages/deltagare-details/deltagare-details.module').then(m => m.DeltagareDetailsModule),
},
{
path: ':genomforandeReferens/signal',
data: { title: 'Skapa signal om arbete eller studier' },
loadChildren: () =>
import(
'./pages/deltagare-details/pages/deltagare-reports/deltagare-signal-arbete-studier/deltagare-signal-arbete-studier.module'
).then(m => m.DeltagareSignalArbeteStudierModule),
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class DeltagareRoutingModule { }
export class DeltagareRoutingModule {}

View File

@@ -9,34 +9,68 @@ const routes: Routes = [
},
{
path: 'avvikelserapport',
data: { title: 'Skapa avvikelserapport' },
data: { title: 'Skapa Avvikelserapport (Avvikelse)' },
loadChildren: () =>
import('./pages/deltagare-reports/deltagare-avvikelserapport/deltagare-avvikelserapport.module').then(
m => m.DeltagareAvvikelserapportModule
import('./pages/report-forms/avvikelse-report-form/avvikelse-report-form.module').then(
m => m.AvvikelseReportFormModule
),
},
{
path: 'avvikelserapport/:reportId',
data: { title: 'Avvikelserapport (avvikelse)' },
loadChildren: () =>
import('./pages/report-views/avvikelse-report-view/avvikelse-report-view.module').then(
m => m.AvvikelseReportViewModule
),
},
{
path: 'franvarorapport',
data: { title: 'Skapa rapport' },
data: { title: 'Skapa Avvikelserapport (frånvaro)' },
loadChildren: () =>
import('./pages/deltagare-reports/franvaro-report/franvaro-report.module').then(m => m.FranvaroReportModule),
import('./pages/report-forms/franvaro-report-form/franvaro-report-form.module').then(
m => m.FranvaroReportFormModule
),
},
{
path: 'franvarorapport/:reportId',
data: { title: 'Avvikelserapport (frånvaro)' },
loadChildren: () =>
import('./pages/report-views/franvaro-report-view/franvaro-report-view.module').then(
m => m.FranvaroReportViewModule
),
},
{
path: 'gemensam-planering',
data: { title: 'Skapa Gemensam planering' },
loadChildren: () =>
import('./pages/deltagare-reports/deltagare-gemensam-planering/deltagare-gemensam-planering.module').then(
m => m.DeltagareGemensamPlaneringModule
import('./pages/report-forms/gemensam-planering-form/gemensam-planering-form.module').then(
m => m.GemensamPlaneringFormModule
),
},
{
path: 'gemensam-planering/:reportId',
data: { title: 'Gemensam planering' },
loadChildren: () =>
import('./pages/report-views/gemensam-planering-view/gemensam-planering-view.module').then(
m => m.GemensamPlaneringViewModule
),
},
{
path: 'periodisk-redovisning',
data: { title: 'Skapa periodisk redovisning' },
loadChildren: () =>
import('./pages/deltagare-reports/deltagare-periodisk-redovisning/deltagare-periodisk-redovisning.module').then(
import('./pages/report-forms/deltagare-periodisk-redovisning/deltagare-periodisk-redovisning.module').then(
m => m.DeltagarePeriodiskRedovisningModule
),
},
{
path: 'signal',
data: { title: 'Skapa signal om arbete eller studier' },
loadChildren: () =>
import('./pages/report-forms/deltagare-signal-arbete-studier/deltagare-signal-arbete-studier.module').then(
m => m.DeltagareSignalArbeteStudierModule
),
},
];
@NgModule({

View File

@@ -9,7 +9,12 @@
<tbody>
<tr *ngFor="let report of reports">
<th scope="row">{{ report.type }}</th>
<th scope="row">
<a *ngIf="getReportLink(report); else reportTypeTextRef" [routerLink]="getReportLink(report)"
>{{ report.type }}</a
>
<ng-template #reportTypeTextRef>{{report.type}}</ng-template>
</th>
<td>{{ report.date | date:'longDate' }} {{ report.date | date:'shortTime' }}</td>
<td>{{ report.status }}</td>
</tr>

View File

@@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { ReportType } from '@msfa-enums/report-type.enum';
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
import { Report } from '@msfa-models/report.model';
@@ -49,6 +50,19 @@ export class ReportsListComponent {
return end < this.count ? end : this.count;
}
getReportLink(report: Report): string {
switch (report.type) {
case ReportType.GemensamPlanering:
return `./gemensam-planering/${report.id}`;
case ReportType.Franvaro:
return `./franvarorapport/${report.id}`;
case ReportType.Avvikelse:
return `./avvikelserapport/${report.id}`;
default:
return null;
}
}
emitNewPage(page: number): void {
this.paginated.emit(page);
}

View File

@@ -1,13 +0,0 @@
import { DayOrPartOfDay } from '@msfa-enums/day-or-part-of-day.enum';
import { RadiobuttonModel } from '@af/digi-ng/_form/form-radiobutton-group';
export const dayOrPartOfDay: RadiobuttonModel[] = [
{
label: 'Heldag',
value: DayOrPartOfDay.HELDAG,
},
{
label: 'Del av dag',
value: DayOrPartOfDay.DEL_AV_DAG,
},
];

View File

@@ -5,13 +5,13 @@
description="Här rapporterar du deltagarens avvikelser. Exempelvis kan du rapportera om tjänsten inte fungerar för deltagaren eller om deltagaren misskött sig under tjänsten."
reportTitle="Avvikelserapport (avvikelse)"
>
<div class="deltagare-avvikelse">
<div *ngIf="submittedDate$ | async as submittedDate; else formRef" class="deltagare-avvikelse__confirmation">
<div class="avvikelse-report-form">
<div *ngIf="submittedDate$ | async as submittedDate; else formRef" class="avvikelse-report-form__confirmation">
<digi-notification-alert
af-heading="Allt gick bra"
af-heading-level="h3"
af-variation="success"
class="deltagare-avvikelse__alert"
class="avvikelse-report-form__alert"
>
<p>Avvikelserapport (avvikelse) för deltagare {{avrop.fullName}} är nu inskickad till Arbetsförmedlingen.</p>
<dl>
@@ -23,8 +23,8 @@
</div>
<ng-template #formRef>
<form class="deltagare-avvikelse__form" [formGroup]="avvikelseFormGroup" (ngSubmit)="openConfirmDialog()">
<div class="deltagare-avvikelse__form-item">
<form class="avvikelse-report-form__form" [formGroup]="avvikelseFormGroup" (ngSubmit)="openConfirmDialog()">
<div class="avvikelse-report-form__form-item">
<digi-ng-form-select
*ngIf="reasonsAsNgDigiFormSelectItems$ | async; let reason; else loadingRef"
[formControlName]="reasonFormName"
@@ -40,12 +40,12 @@
</div>
<div
class="deltagare-avvikelse__form-item deltagare-avvikelse__textareas"
class="avvikelse-report-form__form-item avvikelse-report-form__textareas"
[formArrayName]="questionsFormName"
*ngIf="questionsForChosenReason$ | async; let questions"
>
<div
class="deltagare-avvikelse__form-item"
class="avvikelse-report-form__form-item"
*ngFor="let question of questionsFormArray.controls; let i=index"
>
<digi-ng-form-textarea
@@ -61,7 +61,7 @@
</div>
</div>
<div class="deltagare-avvikelse__form-item" *ngIf="chosenReasonId$ | async">
<div class="avvikelse-report-form__form-item" *ngIf="chosenReasonId$ | async">
<digi-ng-form-datepicker
[afDisableValidStyle]="true"
[afMinDate]="minDate(avrop)"
@@ -75,7 +75,7 @@
></digi-ng-form-datepicker>
</div>
<div class="deltagare-avvikelse__cta-wrapper">
<div class="avvikelse-report-form__cta-wrapper">
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
<msfa-back-link [showIcon]="false" [asButton]="true" route="../">
<span>Avbryt</span>
@@ -134,7 +134,10 @@
</msfa-report-layout>
</msfa-layout>
<ng-template #skeletonRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar data för avvikelserapport"></digi-ng-skeleton-base>
<digi-ng-skeleton-base
[afCount]="3"
afText="Laddar data för att kunna skapa Avvikelserapport (avvikelse)"
></digi-ng-skeleton-base>
</ng-template>
<ng-template #loadingRef>
<msfa-loader type="padded"></msfa-loader>

View File

@@ -6,17 +6,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
import { DeltagareAvvikelserapportComponent } from './deltagare-avvikelserapport.component';
import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.service';
import { AvvikelseReportFormComponent } from './avvikelse-report-form.component';
import { AvvikelseReportFormService } from './avvikelse-report-form.service';
describe('DeltagareAvvikelseComponent', () => {
let component: DeltagareAvvikelserapportComponent;
let fixture: ComponentFixture<DeltagareAvvikelserapportComponent>;
describe('AvvikelseReportFormComponent', () => {
let component: AvvikelseReportFormComponent;
let fixture: ComponentFixture<AvvikelseReportFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [DeltagareAvvikelserapportComponent, LayoutComponent],
declarations: [AvvikelseReportFormComponent, LayoutComponent],
imports: [
RouterTestingModule,
HttpClientTestingModule,
@@ -24,12 +24,12 @@ describe('DeltagareAvvikelseComponent', () => {
DigiNgFormRadiobuttonGroupModule,
DigiNgFormDatepickerModule,
],
providers: [DeltagareAvvikelserapportService],
providers: [AvvikelseReportFormService],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DeltagareAvvikelserapportComponent);
fixture = TestBed.createComponent(AvvikelseReportFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -3,17 +3,16 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Avrop } from '@msfa-models/avrop.model';
import { AvvikelseAlternativ, AvvikelseRequestData } from '@msfa-models/avvikelse.model';
import { AvvikelseAlternativ_OLD, AvvikelseRequestData_OLD } from '@msfa-models/avvikelse.model';
import { CustomError } from '@msfa-models/error/custom-error';
import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model';
import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model';
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
import { markControlsAsDirty } from '@msfa-utils/mark-controls-as-dirty.util';
import { RegexValidator } from '@msfa-utils/validators/regex.validator';
import { RequiredValidator } from '@msfa-validators/required.validator';
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.service';
import { AvvikelseReportFormService } from './avvikelse-report-form.service';
interface Params {
genomforandeReferens: string;
@@ -28,12 +27,12 @@ interface AvvikelseFormData {
type AvvikelseFormKeys = keyof AvvikelseFormData;
@Component({
selector: 'msfa-deltagare-avvikelse',
templateUrl: './deltagare-avvikelserapport.component.html',
styleUrls: ['./deltagare-avvikelserapport.component.scss'],
selector: 'msfa-avvikelse-report-form',
templateUrl: './avvikelse-report-form.component.html',
styleUrls: ['./avvikelse-report-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DeltagareAvvikelserapportComponent implements OnInit, OnDestroy {
export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
shouldValidate$ = new BehaviorSubject<boolean>(false);
reasonFormName: AvvikelseFormKeys = 'reason';
@@ -46,20 +45,20 @@ export class DeltagareAvvikelserapportComponent implements OnInit, OnDestroy {
map((params: Params) => +params.genomforandeReferens)
);
avrop$: Observable<Avrop> = this.genomforandeReferens$.pipe(
switchMap(genomforandeReferens => this.deltagareAvvikelseService.fetchAvropInformation$(genomforandeReferens)),
switchMap(genomforandeReferens => this.avvikelseReportFormService.fetchAvropInformation$(genomforandeReferens)),
shareReplay(1)
);
reasons$: Observable<OrsaksKoderAvvikelse[]> = this.deltagareAvvikelseService.getAvvikelseOrsaker$;
reasons$: Observable<OrsaksKoderAvvikelse[]> = this.avvikelseReportFormService.getAvvikelseOrsaker$;
reasonsAsNgDigiFormSelectItems$: Observable<FormSelectItem[]> = this.reasons$.pipe(
map(reasons => reasons.map(reason => ({ name: reason.name, value: reason.id })))
);
allAvvikelseQuestions$ = this.deltagareAvvikelseService.fragorForAvvikelser$;
allAvvikelseQuestions$ = this.avvikelseReportFormService.fragorForAvvikelser$;
chosenReasonId$: Observable<string>;
chosenReason$: Observable<OrsaksKoderAvvikelse>;
questionsForChosenReason$: Observable<FragorForAvvikelser[]>;
avvikelseSubmitData$: Observable<AvvikelseRequestData>;
avvikelseSubmitData$: Observable<AvvikelseRequestData_OLD>;
confirmDialogIsOpen$ = new BehaviorSubject<boolean>(false);
submittedDate$ = new BehaviorSubject<Date | null>(null);
private subscriptions: Subscription[] = [];
@@ -73,11 +72,7 @@ export class DeltagareAvvikelserapportComponent implements OnInit, OnDestroy {
.valueChanges as Observable<AvvikelseFormData>;
private currentQuestions: FragorForAvvikelser[];
constructor(
private deltagareAvvikelseService: DeltagareAvvikelserapportService,
private deltagareApiService: DeltagareApiService,
private activatedRoute: ActivatedRoute
) {}
constructor(private avvikelseReportFormService: AvvikelseReportFormService, private activatedRoute: ActivatedRoute) {}
get reasonFormControl(): AbstractControl | undefined {
return this.avvikelseFormGroup.get(this.reasonFormName);
@@ -154,7 +149,7 @@ export class DeltagareAvvikelserapportComponent implements OnInit, OnDestroy {
submitAndCloseConfirmDialog(): void {
this.submitIsLoading$.next(true);
this.avvikelseSubmitData$.pipe(take(1)).subscribe(avvikelseSubmitData =>
this.deltagareAvvikelseService.createAvvikelse$(avvikelseSubmitData).subscribe({
this.avvikelseReportFormService.createAvvikelse$(avvikelseSubmitData).subscribe({
next: () => {
this.submitIsLoading$.next(false);
this.submittedDate$.next(new Date());
@@ -180,15 +175,15 @@ export class DeltagareAvvikelserapportComponent implements OnInit, OnDestroy {
genomforandeReferens: number,
chosenReason: string,
formData: AvvikelseFormData
): AvvikelseRequestData {
const avvikelseAlternativ: AvvikelseAlternativ = {
): AvvikelseRequestData_OLD {
const avvikelseAlternativ: AvvikelseAlternativ_OLD = {
avvikelseorsakskod: chosenReason,
frageformular: formData.questions.map((question, index) => ({
fraga: this.currentQuestions[index].id,
svar: question,
})),
rapporteringsdatum: formData.reportingDate,
} as AvvikelseAlternativ;
} as AvvikelseAlternativ_OLD;
return { genomforandeReferens, avvikelseAlternativ };
}

View File

@@ -15,16 +15,16 @@ import { ConfirmDialogModule } from '@msfa-shared/components/confirm-dialog/conf
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
import { DeltagareAvvikelserapportComponent } from './deltagare-avvikelserapport.component';
import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.service';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { AvvikelseReportFormComponent } from './avvikelse-report-form.component';
import { AvvikelseReportFormService } from './avvikelse-report-form.service';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [DeltagareAvvikelserapportComponent],
declarations: [AvvikelseReportFormComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: DeltagareAvvikelserapportComponent }]),
RouterModule.forChild([{ path: '', component: AvvikelseReportFormComponent }]),
LayoutModule,
ReactiveFormsModule,
DigiNgFormRadiobuttonGroupModule,
@@ -41,7 +41,7 @@ import { DeltagareAvvikelserapportService } from './deltagare-avvikelserapport.s
DigiNgFormInputModule,
DigiNgDialogModule,
],
providers: [DeltagareAvvikelserapportService],
exports: [DeltagareAvvikelserapportComponent],
providers: [AvvikelseReportFormService],
exports: [AvvikelseReportFormComponent],
})
export class DeltagareAvvikelserapportModule {}
export class AvvikelseReportFormModule {}

View File

@@ -1,14 +1,14 @@
import { Injectable } from '@angular/core';
import { Avvikelse } from '@msfa-models/avvikelse.model';
import { Avvikelse_OLD } from '@msfa-models/avvikelse.model';
import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model';
import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model';
import { AvvikelseApiService } from '@msfa-services/api/avvikelse-api.service';
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
@Injectable()
export class DeltagareAvvikelserapportService {
export class AvvikelseReportFormService {
fragorForAvvikelser$: Observable<FragorForAvvikelser[]> = this.avvikelseApiService
.getFragorForAvvikelser$()
.pipe(shareReplay(1));
@@ -17,7 +17,7 @@ export class DeltagareAvvikelserapportService {
constructor(private avvikelseApiService: AvvikelseApiService, private deltagareApiService: DeltagareApiService) {}
createAvvikelse$(avvikelse: Avvikelse): Observable<unknown> {
createAvvikelse$(avvikelse: Avvikelse_OLD): Observable<unknown> {
return this.avvikelseApiService.createAvvikelse$(avvikelse);
}

View File

@@ -4,7 +4,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { PeriodiskRedovisningFormModule } from './components/periodisk-redovisning-form/periodisk-redovisning-form.module';
import { DeltagarePeriodiskRedovisningComponent } from './deltagare-periodisk-redovisning.component';

View File

@@ -10,7 +10,7 @@ import { RouterModule } from '@angular/router';
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
import { ConfirmDialogModule } from '@msfa-shared/components/confirm-dialog/confirm-dialog.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { DeltagareConfirmSignalFormModule } from './components/deltagare-confirm-signal-form/deltagare-confirm-signal-form.module';
import { DeltagareSignalArbeteStudierComponent } from './deltagare-signal-arbete-studier.component';
@@ -30,8 +30,8 @@ import { DeltagareSignalArbeteStudierComponent } from './deltagare-signal-arbete
ReportLayoutModule,
ConfirmDialogModule,
DigiNgSkeletonBaseModule,
BackLinkModule
BackLinkModule,
],
exports: [DeltagareSignalArbeteStudierComponent],
})
export class DeltagareSignalArbeteStudierModule { }
export class DeltagareSignalArbeteStudierModule {}

View File

@@ -5,8 +5,8 @@
description="Här rapporterar du deltagarens frånvaro i tjänsten."
reportTitle="Avvikelserapport (frånvaro)"
>
<div class="franvaro-report" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
<div class="franvaro-report__warning" *ngIf="maxDate < avrop.startDate; else reportRef">
<div class="franvaro-report-form" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
<div class="franvaro-report-form__warning" *ngIf="maxDate < avrop.startDate; else reportRef">
<digi-notification-alert af-variation="warning" af-heading="Kan inte skapa Avvikelserapport (frånvaro)">
<p>Det går inte att rapportera frånvaro eftersom tjänsten inte har startat ännu.</p>
</digi-notification-alert>
@@ -16,7 +16,7 @@
<ng-template #reportRef>
<div
class="franvaro-report__confirmation"
class="franvaro-report-form__confirmation"
*ngIf="lastSubmittedFranvaroReport$ | async as lastSubmittedFranvaroReport; else formRef"
>
<digi-notification-alert af-variation="success" af-heading="Allt gick bra" af-heading-level="h3">
@@ -32,12 +32,12 @@
</div>
<ng-template #formRef>
<form
class="franvaro-report__form"
class="franvaro-report-form__form"
[formGroup]="franvaroFormGroup"
(ngSubmit)="openConfirmDialog()"
id="franvaro-report-form"
>
<div class="franvaro-report__form-item">
<div class="franvaro-report-form__form-item">
<digi-ng-form-select
*ngIf="reasons$ | async as reasons; else loadingRef"
[formControl]="reasonFormControl"
@@ -53,7 +53,7 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['reason'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.reason"
></digi-ng-form-validation-message>
@@ -63,7 +63,7 @@
<ng-container *ngIf="reasonFormControl.value">
<ng-container *ngIf="showOtherKnownReasonsSelect">
<div
class="franvaro-report__form-item"
class="franvaro-report-form__form-item"
*ngIf="otherKnownReasons$ | async as otherKnownReasons; else loadingRef"
>
<digi-ng-form-select
@@ -79,14 +79,14 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['otherKnownReason'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.otherKnownReason"
></digi-ng-form-validation-message>
</div>
</div>
<div class="franvaro-report__form-item" *ngIf="showKnownReasonTextArea">
<div class="franvaro-report-form__form-item" *ngIf="showKnownReasonTextArea">
<digi-ng-form-textarea
[afDisableValidStyle]="true"
[afInvalid]="formControlIsInvalid(['knownReasonComment'])"
@@ -100,7 +100,7 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['knownReasonComment'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.knownReasonComment"
></digi-ng-form-validation-message>
@@ -108,7 +108,7 @@
</div>
</ng-container>
<div class="franvaro-report__form-item">
<div class="franvaro-report-form__form-item">
<digi-ng-form-datepicker
[afDisableValidStyle]="true"
[afMinDate]="avrop.startDate"
@@ -122,14 +122,14 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['date'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.date"
></digi-ng-form-validation-message>
</div>
</div>
<div class="franvaro-report__form-item">
<div class="franvaro-report-form__form-item">
<h2>Tiden deltagaren var frånvarande</h2>
<digi-form-fieldset
af-legend="Heldag eller del av dag"
@@ -145,8 +145,8 @@
</div>
<ng-container *ngIf="showTimePickers">
<div class="franvaro-report__time-pickers">
<div class="franvaro-report__time-picker">
<div class="franvaro-report-form__time-pickers">
<div class="franvaro-report-form__time-picker">
<digi-ng-form-input
afLabel="Starttid"
[formControl]="startTimeFormControl"
@@ -159,13 +159,13 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['startTime'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.startTime"
></digi-ng-form-validation-message>
</div>
</div>
<div class="franvaro-report__time-picker">
<div class="franvaro-report-form__time-picker">
<digi-ng-form-input
afLabel="Sluttid"
[formControl]="endTimeFormControl"
@@ -178,7 +178,7 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['endTime'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.endTime"
></digi-ng-form-validation-message>
@@ -188,7 +188,7 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['expectedEndTimeIsBeforeStartTime'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.expectedEndTimeIsBeforeStartTime"
></digi-ng-form-validation-message>
@@ -197,8 +197,8 @@
<div>
<h2>Tiden deltagaren skulle varit närvarande</h2>
<div class="franvaro-report__time-pickers">
<div class="franvaro-report__time-picker">
<div class="franvaro-report-form__time-pickers">
<div class="franvaro-report-form__time-picker">
<digi-ng-form-input
afLabel="Starttid"
[formControl]="expectedPresenceStartTimeFormControl"
@@ -211,13 +211,13 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['expectedPresenceStartTime'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.expectedPresenceStartTime"
></digi-ng-form-validation-message>
</div>
</div>
<div class="franvaro-report__time-picker">
<div class="franvaro-report-form__time-picker">
<digi-ng-form-input
afLabel="Sluttid"
[formControl]="expectedPresenceEndTimeFormControl"
@@ -230,7 +230,7 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['expectedPresenceEndTime'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.expectedPresenceEndTime"
></digi-ng-form-validation-message>
@@ -240,7 +240,7 @@
<div aria-atomic="true" role="alert">
<digi-ng-form-validation-message
*ngIf="formControlIsInvalid(['expectedPresenceEndTimeIsBeforeStartTime'])"
class="franvaro-report__validation-message"
class="franvaro-report-form__validation-message"
[afPositive]="false"
[afValidationText]="formErrors.expectedPresenceEndTimeIsBeforeStartTime"
></digi-ng-form-validation-message>
@@ -248,17 +248,17 @@
</div>
</ng-container>
<footer class="franvaro-report__footer">
<footer class="franvaro-report-form__footer">
<digi-notification-alert
*ngIf="error$ | async as error"
class="franvaro-report__alert"
class="franvaro-report-form__alert"
af-variation="danger"
af-heading="Någonting gick fel"
>
<p>Kunde inte spara Avvikelserapport (frånvaro). Ladda om sidan och försök igen.</p>
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
</digi-notification-alert>
<div class="franvaro-report__cta-wrapper">
<div class="franvaro-report-form__cta-wrapper">
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
<msfa-back-link [showIcon]="false" [asButton]="true" route="../">
<span>Avbryt</span>
@@ -334,7 +334,10 @@
</msfa-layout>
<ng-template #skeletonRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar data för Avvikelserapport (frånvaro)"></digi-ng-skeleton-base>
<digi-ng-skeleton-base
[afCount]="3"
afText="Laddar data för att kunna skapa Avvikelserapport (frånvaro)"
></digi-ng-skeleton-base>
</ng-template>
<ng-template #loadingRef>

View File

@@ -1,7 +1,7 @@
@import 'variables/gutters';
@import 'variables/z-index';
.franvaro-report {
.franvaro-report-form {
max-width: var(--digi--typography--text--max-width);
&__confirmation,

View File

@@ -3,24 +3,24 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
import { FranvaroReportComponent } from './franvaro-report.component';
import { FranvaroReportService } from './franvaro-report.service';
import { FranvaroReportFormComponent } from './franvaro-report-form.component';
import { FranvaroReportFormService } from './franvaro-report-form.service';
describe('FranvaroReportComponent', () => {
let component: FranvaroReportComponent;
let fixture: ComponentFixture<FranvaroReportComponent>;
describe('FranvaroReportFormComponent', () => {
let component: FranvaroReportFormComponent;
let fixture: ComponentFixture<FranvaroReportFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [FranvaroReportComponent, LayoutComponent],
declarations: [FranvaroReportFormComponent, LayoutComponent],
imports: [RouterTestingModule, HttpClientTestingModule],
providers: [FranvaroReportService],
providers: [FranvaroReportFormService],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FranvaroReportComponent);
fixture = TestBed.createComponent(FranvaroReportFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -5,23 +5,23 @@ import { ActivatedRoute } from '@angular/router';
import { ANNAN_KAND_ORSAK_ID, ANNAN_ORSAK_ID } from '@msfa-constants/franvaro-reasons';
import { ErrorType } from '@msfa-enums/error-type.enum';
import { Avrop } from '@msfa-models/avrop.model';
import { FranvaroRequestData } from '@msfa-models/avvikelse.model';
import { FranvaroRequestData_OLD } from '@msfa-models/avvikelse.model';
import { CustomError } from '@msfa-models/error/custom-error';
import { Franvaro } from '@msfa-models/franvaro.model';
import { OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
import { dateToIsoString } from '@msfa-utils/format-to-date.util';
import { BehaviorSubject, Observable } from 'rxjs';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import { FranvaroReportService } from './franvaro-report.service';
import { FranvaroReportValidator } from './franvaro-report.validator';
import { FranvaroReportFormService } from './franvaro-report-form.service';
import { FranvaroReportFormValidator } from './franvaro-report-form.validator';
@Component({
selector: 'msfa-franvaro-report',
templateUrl: './franvaro-report.component.html',
styleUrls: ['./franvaro-report.component.scss'],
selector: 'msfa-franvaro-report-form',
templateUrl: './franvaro-report-form.component.html',
styleUrls: ['./franvaro-report-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FranvaroReportComponent {
export class FranvaroReportFormComponent {
maxDate = new Date();
shouldValidate$ = new BehaviorSubject<boolean>(false);
confirmDialogOpen$ = new BehaviorSubject<boolean>(false);
@@ -37,7 +37,7 @@ export class FranvaroReportComponent {
expectedPresenceStartTime: new FormControl(null),
expectedPresenceEndTime: new FormControl(null),
},
[FranvaroReportValidator.isFranvaroReportValid()]
[FranvaroReportFormValidator.isFranvaroReportValid()]
);
error$ = new BehaviorSubject<CustomError>(null);
@@ -47,18 +47,18 @@ export class FranvaroReportComponent {
map(params => params.genomforandeReferens as string)
);
avrop$: Observable<Avrop> = this.currentGenomforandeReferens$.pipe(
switchMap(genomforandeReferens => this.franvaroReportService.fetchAvropInformation$(+genomforandeReferens)),
switchMap(genomforandeReferens => this.franvaroReportFormService.fetchAvropInformation$(+genomforandeReferens)),
shareReplay(1)
);
reasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportService.reasons$;
otherKnownReasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportService.otherKnownReasons$;
reasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportFormService.reasons$;
otherKnownReasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportFormService.otherKnownReasons$;
wholeDayOrPartOfDayRadiobuttons: RadiobuttonModel[] = [
{ label: 'Heldag', value: true },
{ label: 'Del av dag', value: false },
];
constructor(private franvaroReportService: FranvaroReportService, private activatedRoute: ActivatedRoute) {
constructor(private franvaroReportFormService: FranvaroReportFormService, private activatedRoute: ActivatedRoute) {
this.dateFormControl.valueChanges.subscribe(value => console.log(value));
}
@@ -160,7 +160,7 @@ export class FranvaroReportComponent {
expectedPresenceEndTime,
} = this.franvaroFormGroup.value as Franvaro;
const postRequest: FranvaroRequestData = {
const postRequest: FranvaroRequestData_OLD = {
genomforandeReferens,
franvaro: {
avvikelseOrsaksKod: reason,
@@ -181,7 +181,7 @@ export class FranvaroReportComponent {
},
};
return this.franvaroReportService
return this.franvaroReportFormService
.postFranvaroReport(postRequest)
.then(() => {
this.lastSubmittedFranvaroReport$.next(new Date());

View File

@@ -14,16 +14,16 @@ import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.modu
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
import { FranvaroReportComponent } from './franvaro-report.component';
import { FranvaroReportService } from './franvaro-report.service';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { FranvaroReportFormComponent } from './franvaro-report-form.component';
import { FranvaroReportFormService } from './franvaro-report-form.service';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [FranvaroReportComponent],
declarations: [FranvaroReportFormComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: FranvaroReportComponent }]),
RouterModule.forChild([{ path: '', component: FranvaroReportFormComponent }]),
ReactiveFormsModule,
LayoutModule,
ReportLayoutModule,
@@ -39,7 +39,7 @@ import { FranvaroReportService } from './franvaro-report.service';
DigiNgFormValidationMessageModule,
DigiNgDialogModule,
],
providers: [FranvaroReportService],
exports: [FranvaroReportComponent],
providers: [FranvaroReportFormService],
exports: [FranvaroReportFormComponent],
})
export class FranvaroReportModule {}
export class FranvaroReportFormModule {}

View File

@@ -1,12 +1,12 @@
import { Injectable } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { FranvaroRequestData } from '@msfa-models/avvikelse.model';
import { FranvaroRequestData_OLD } from '@msfa-models/avvikelse.model';
import { OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
import { FranvaroReportApiService } from '@msfa-services/api/franvaro-report.api.service';
import { Observable } from 'rxjs';
@Injectable()
export class FranvaroReportService {
export class FranvaroReportFormService {
public reasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportApiService.fetchReasons$();
public otherKnownReasons$: Observable<
OrsaksKoderFranvaro[]
@@ -18,7 +18,7 @@ export class FranvaroReportService {
return this.franvaroReportApiService.fetchAvropInformation$(genomforandeReferens);
}
public async postFranvaroReport(requestData: FranvaroRequestData): Promise<void> {
public async postFranvaroReport(requestData: FranvaroRequestData_OLD): Promise<void> {
return this.franvaroReportApiService.postFranvaroReport$(requestData);
}
}

View File

@@ -9,7 +9,7 @@ function isTimeValid(value: string): boolean {
return TIME_REGEX.test(value);
}
export class FranvaroReportValidator {
export class FranvaroReportFormValidator {
static isFranvaroReportValid(): ValidatorFn {
return (c: AbstractControl): { [key: string]: string } => {
let errors: { [key: string]: string } = null;

View File

@@ -1,11 +1,11 @@
<msfa-layout>
<msfa-report-layout
*ngIf="avrop$ | async as avrop; else skeletonRef"
reportTitle="Gemensam planering"
reportTitle="Skapa Gemensam planering"
[avrop]="avrop"
>
<div class="gemensam-planering" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
<div class="gemensam-planering__warning" *ngIf="today < avrop.startDate; else reportRef">
<div class="gemensam-planering-form" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
<div class="gemensam-planering-form__warning" *ngIf="today < avrop.startDate; else reportRef">
<digi-notification-alert af-variation="warning" af-heading="Kan inte skapa Gemensam planering">
<p>Det går inte att skicka Gemensam planering eftersom tjänsten inte har startat ännu.</p>
</digi-notification-alert>
@@ -13,9 +13,12 @@
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>
</div>
<ng-template #reportRef>
<div class="gemensam-planering__confirmation" *ngIf="lastSubmittedGP$ | async as lastSubmittedGP; else formRef">
<div
class="gemensam-planering-form__confirmation"
*ngIf="lastSubmittedGP$ | async as lastSubmittedGP; else formRef"
>
<digi-notification-alert
class="gemensam-planering__alert"
class="gemensam-planering-form__alert"
af-variation="success"
af-heading="Allt gick bra"
af-heading-level="h3"
@@ -33,15 +36,15 @@
</div>
<ng-template #formRef>
<form
class="gemensam-planering__form"
class="gemensam-planering-form__form"
[formGroup]="gpFormGroup"
(ngSubmit)="openConfirmDialog()"
id="gemensam-planering-form"
id="gemensam-planering-form-form"
>
<digi-form-fieldset
af-legend="Deltar arbetssökande på distans?"
af-name="distance"
af-form="gemensam-planering-form"
af-form="gemensam-planering-form-form"
>
<digi-ng-form-radiobutton-group
[afRadiobuttons]="distanceRadiobuttons"
@@ -50,15 +53,15 @@
[afRadiobuttonGroupDirection]="RadiobuttonGroupDirection.HORIZONTAL"
></digi-ng-form-radiobutton-group>
</digi-form-fieldset>
<digi-form-fieldset af-legend="Aktiviteter" af-name="aktivitetsIds" af-form="gemensam-planering-form">
<digi-form-fieldset af-legend="Aktiviteter" af-name="aktivitetsIds" af-form="gemensam-planering-form-form">
<p>
Varje Gemensam planering måste innehålla de två obligatoriska aktiviteterna samt en frivillig aktivitet
som en del av det individuella stödet för varje deltagare.
</p>
<ng-container *ngIf="activities$ | async as activities; else loadingRef">
<ul class="gemensam-planering__activity-list">
<li class="gemensam-planering__activity-item" *ngFor="let activity of activities;">
<ul class="gemensam-planering-form__activity-list">
<li class="gemensam-planering-form__activity-item" *ngFor="let activity of activities;">
<digi-form-checkbox
[afLabel]="activity.name + (isActivityObligatory(activity.id) ? ' (obligatorisk)' : '')"
[afValue]="activity.id"
@@ -74,17 +77,17 @@
</ng-container>
</digi-form-fieldset>
<footer class="gemensam-planering__footer">
<footer class="gemensam-planering-form__footer">
<digi-notification-alert
*ngIf="error$ | async as error"
class="gemensam-planering__alert"
class="gemensam-planering-form__alert"
af-variation="danger"
af-heading="Någonting gick fel"
>
<p>Kunde inte spara Gemensam planering. Ladda om sidan och försök igen.</p>
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
</digi-notification-alert>
<div class="gemensam-planering__cta-wrapper">
<div class="gemensam-planering-form__cta-wrapper">
<digi-button af-type="submit" af-size="m">Förhandsgranska</digi-button>
<msfa-back-link [showIcon]="false" [asButton]="true" route="../">
<span>Avbryt</span>
@@ -103,7 +106,7 @@
(afOnSecondaryClick)="cancelConfirmDialog()"
afHeading="Vill du skicka in Gemensam planering"
afAriaLabel="Förhandsgranska och skicka in Gemensam planering"
id="confirm-gemensam-planering"
id="confirm-gemensam-planering-form"
>
<msfa-loader *ngIf="submitLoading$ | async" type="absolute"></msfa-loader>
<dl>
@@ -131,13 +134,13 @@
<dd>{{gpFormGroup.value.distance ? 'Ja' : 'Nej'}}</dd>
<dt>Aktiviteter</dt>
<dd>
<ul class="gemensam-planering__activity-list" *ngFor="let activity of activities$ | async">
<ul class="gemensam-planering-form__activity-list" *ngFor="let activity of activities$ | async">
<li
class="gemensam-planering__activity-item"
class="gemensam-planering-form__activity-item"
*ngIf="activityIdsFormArray.value.includes(activity.id)"
>
<digi-icon-check-circle
class="msfa__digi-icon gemensam-planering__activity-check"
class="msfa__digi-icon gemensam-planering-form__activity-check"
aria-hidden="true"
></digi-icon-check-circle>
{{activity.name}}
@@ -153,7 +156,10 @@
</msfa-layout>
<ng-template #skeletonRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar data för Gemensam planering"></digi-ng-skeleton-base>
<digi-ng-skeleton-base
[afCount]="3"
afText="Laddar data för att kunna skapa Gemensam planering"
></digi-ng-skeleton-base>
</ng-template>
<ng-template #loadingRef>

View File

@@ -1,31 +1,33 @@
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
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 { RouterTestingModule } from '@angular/router/testing';
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
import { DeltagareGemensamPlaneringComponent } from './deltagare-gemensam-planering.component';
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
import { GemensamPlaneringFormComponent } from './gemensam-planering-form.component';
import { GemensamPlaneringFormService } from './gemensam-planering-form.service';
describe('DeltagareGemensamPlaneringComponent', () => {
let component: DeltagareGemensamPlaneringComponent;
let fixture: ComponentFixture<DeltagareGemensamPlaneringComponent>;
describe('GemensamPlaneringFormComponent', () => {
let component: GemensamPlaneringFormComponent;
let fixture: ComponentFixture<GemensamPlaneringFormComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [DeltagareGemensamPlaneringComponent, LayoutComponent],
declarations: [GemensamPlaneringFormComponent, LayoutComponent],
imports: [
RouterTestingModule,
HttpClientTestingModule,
DigiNgFormRadiobuttonGroupModule,
DigiNgFormCheckboxModule,
],
providers: [GemensamPlaneringFormService],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DeltagareGemensamPlaneringComponent);
fixture = TestBed.createComponent(GemensamPlaneringFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -11,17 +11,17 @@ import {
mapGemensamPlaneringToGemensamPlaneringPostRequest,
} from '@msfa-models/gemensam-planering.model';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map, shareReplay, switchMap } from 'rxjs/operators';
import { GemensamPlaneringService } from './gemensam-planering.service';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import { GemensamPlaneringFormService } from './gemensam-planering-form.service';
import { GemensamPlaneringValidator } from './gemensam-planering.validator';
@Component({
selector: 'msfa-deltagare-gemensam-planering',
templateUrl: './deltagare-gemensam-planering.component.html',
styleUrls: ['./deltagare-gemensam-planering.component.scss'],
selector: 'msfa-gemensam-planering-form',
templateUrl: './gemensam-planering-form.component.html',
styleUrls: ['./gemensam-planering-form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DeltagareGemensamPlaneringComponent {
export class GemensamPlaneringFormComponent {
obligatoryActivityIds = [165, 188];
shouldValidate$ = new BehaviorSubject<boolean>(false);
RadiobuttonGroupDirection = RadiobuttonGroupDirection;
@@ -31,16 +31,12 @@ export class DeltagareGemensamPlaneringComponent {
lastSubmittedGP$ = new BehaviorSubject<Date>(null);
submitLoading$ = new BehaviorSubject<boolean>(false);
activities$: Observable<Activity[]> = this.gemensamPlaneringService.activities$;
currentGenomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
map(params => params.genomforandeReferens as string),
distinctUntilChanged(
([prevGenomforandeReferens], [currGenomforandeReferens]) => prevGenomforandeReferens === currGenomforandeReferens
),
map(genomforandeReferens => +genomforandeReferens)
activities$: Observable<Activity[]> = this.gemensamPlaneringFormService.activities$;
currentGenomforandeReferens$: Observable<string> = this.activatedRoute.params.pipe(
map(params => params.genomforandeReferens as string)
);
avrop$: Observable<Avrop> = this.currentGenomforandeReferens$.pipe(
switchMap(genomforandeReferens => this.gemensamPlaneringService.fetchAvropInformation$(genomforandeReferens)),
switchMap(genomforandeReferens => this.gemensamPlaneringFormService.fetchAvropInformation$(+genomforandeReferens)),
shareReplay(1)
);
@@ -94,7 +90,10 @@ export class DeltagareGemensamPlaneringComponent {
return this.gpFormGroup.errors && this.gpFormGroup.errors[formControlName] && this.shouldValidate$.getValue();
}
constructor(private gemensamPlaneringService: GemensamPlaneringService, private activatedRoute: ActivatedRoute) {}
constructor(
private gemensamPlaneringFormService: GemensamPlaneringFormService,
private activatedRoute: ActivatedRoute
) {}
updateActivityIds(activityId: string, checked: boolean): void {
if (checked) {
@@ -132,7 +131,7 @@ export class DeltagareGemensamPlaneringComponent {
genomforandeReferens,
};
return this.gemensamPlaneringService
return this.gemensamPlaneringFormService
.postGemensamPlanering(mapGemensamPlaneringToGemensamPlaneringPostRequest(postRequest))
.then(() => {
this.lastSubmittedGP$.next(new Date());

View File

@@ -11,15 +11,16 @@ import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.modu
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
import { ReportLayoutModule } from '../components/report-layout/report-layout.module';
import { DeltagareGemensamPlaneringComponent } from './deltagare-gemensam-planering.component';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { GemensamPlaneringFormComponent } from './gemensam-planering-form.component';
import { GemensamPlaneringFormService } from './gemensam-planering-form.service';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [DeltagareGemensamPlaneringComponent],
declarations: [GemensamPlaneringFormComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: DeltagareGemensamPlaneringComponent }]),
RouterModule.forChild([{ path: '', component: GemensamPlaneringFormComponent }]),
LayoutModule,
DigiNgProgressProgressbarModule,
DigiNgFormRadiobuttonGroupModule,
@@ -32,6 +33,7 @@ import { DeltagareGemensamPlaneringComponent } from './deltagare-gemensam-planer
DigiNgFormCheckboxModule,
DigiNgDialogModule,
],
exports: [DeltagareGemensamPlaneringComponent],
providers: [GemensamPlaneringFormService],
exports: [GemensamPlaneringFormComponent],
})
export class DeltagareGemensamPlaneringModule {}
export class GemensamPlaneringFormModule {}

View File

@@ -5,10 +5,8 @@ import { Avrop } from '@msfa-models/avrop.model';
import { GemensamPlaneringApiService } from '@msfa-services/api/gemensam-planering-api.service';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class GemensamPlaneringService {
@Injectable()
export class GemensamPlaneringFormService {
public activities$: Observable<Activity[]> = this.gemensamPlaneringApiService.fetchActivities$();
constructor(private gemensamPlaneringApiService: GemensamPlaneringApiService) {}

View File

@@ -0,0 +1,31 @@
<msfa-layout>
<msfa-report-layout
*ngIf="avrop$ | async as avrop; else skeletonRef"
reportTitle="Avvikelserapport (avvikelse)"
[avrop]="avrop"
>
<div class="avvikelse-report-view" *ngIf="avvikelseReport$ | async as report; else loadingRef">
<dl *ngIf="report.avvikelse as avvikelse">
<dt>Orsak till avvikelse:</dt>
<dd>{{avvikelse.reason}}</dd>
<ng-container *ngFor="let question of avvikelse.questions">
<dt>{{question.question}}</dt>
<dd>{{question.answer || 'Inget svar' }}</dd>
</ng-container>
<dt>Dag för avvikelse:</dt>
<dd><digi-typography-time [afDateTime]="avvikelse.date"></digi-typography-time></dd>
</dl>
<footer class="avvikelse-report-view__footer">
<msfa-back-link route="../../">Tillbaka till deltagaren</msfa-back-link>
</footer>
</div>
</msfa-report-layout>
</msfa-layout>
<ng-template #skeletonRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar Avvikelserapport (avvikelse)"></digi-ng-skeleton-base>
</ng-template>
<ng-template #loadingRef>
<msfa-loader type="padded"></msfa-loader>
</ng-template>

View File

@@ -0,0 +1,31 @@
@import 'mixins/list';
@import 'variables/gutters';
.avvikelse-report-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);
}
}

View File

@@ -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 { AvvikelseReportViewComponent } from './avvikelse-report-view.component';
import { AvvikelseReportViewService } from './avvikelse-report-view.service';
describe('AvvikelseReportViewComponent', () => {
let component: AvvikelseReportViewComponent;
let fixture: ComponentFixture<AvvikelseReportViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [AvvikelseReportViewComponent, LayoutComponent],
imports: [RouterTestingModule, HttpClientTestingModule],
providers: [AvvikelseReportViewService],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AvvikelseReportViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
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 { AvvikelseReport } from '@msfa-models/avvikelse.model';
import { Observable } from 'rxjs';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import { AvvikelseReportViewService } from './avvikelse-report-view.service';
@Component({
selector: 'msfa-avvikelse-report-view',
templateUrl: './avvikelse-report-view.component.html',
styleUrls: ['./avvikelse-report-view.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvvikelseReportViewComponent {
params$: Observable<Params> = this.activatedRoute.params.pipe(
map(params => ({
reportId: params.reportId as string,
genomforandeReferens: params.genomforandeReferens as string,
}))
);
avrop$: Observable<Avrop> = this.params$.pipe(
switchMap(({ genomforandeReferens }) =>
this.avvikelseReportViewService.fetchAvropInformation$(+genomforandeReferens)
),
shareReplay(1)
);
avvikelseReport$: Observable<AvvikelseReport> = this.params$.pipe(
switchMap(({ genomforandeReferens, reportId }) =>
this.avvikelseReportViewService.fetchAvvikelseReport$(+genomforandeReferens, reportId as string)
),
shareReplay(1)
);
constructor(private avvikelseReportViewService: AvvikelseReportViewService, private activatedRoute: ActivatedRoute) {}
}

View File

@@ -0,0 +1,29 @@
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
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 { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { AvvikelseReportViewComponent } from './avvikelse-report-view.component';
import { AvvikelseReportViewService } from './avvikelse-report-view.service';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [AvvikelseReportViewComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: AvvikelseReportViewComponent }]),
LayoutModule,
ReportLayoutModule,
BackLinkModule,
LoaderModule,
HideTextModule,
DigiNgSkeletonBaseModule,
],
providers: [AvvikelseReportViewService],
exports: [AvvikelseReportViewComponent],
})
export class AvvikelseReportViewModule {}

View File

@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { AvvikelseReport } from '@msfa-models/avvikelse.model';
import { ReportApiService } from '@msfa-services/api/report.api.service';
import { Observable } from 'rxjs';
@Injectable()
export class AvvikelseReportViewService {
constructor(private reportApiService: ReportApiService) {}
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
return this.reportApiService.fetchAvropInformation$(genomforandeReferens);
}
public fetchAvvikelseReport$(genomforandeReferens: number, reportId: string): Observable<AvvikelseReport> {
return this.reportApiService.fetchAvvikelseReport$(genomforandeReferens, reportId);
}
}

View File

@@ -0,0 +1,47 @@
<msfa-layout>
<msfa-report-layout
*ngIf="avrop$ | async as avrop; else skeletonRef"
reportTitle="Avvikelserapport (frånvaro)"
[avrop]="avrop"
>
<div class="franvaro-report-view" *ngIf="franvaroReport$ | async as report; else loadingRef">
<dl>
<ng-container *ngIf="report.franvaro as franvaro">
<dt>Orsak till frånvaro</dt>
<dd>{{franvaro.reason}}</dd>
<ng-container *ngIf="franvaro.otherKnownReason">
<dt>Annan känd orsak</dt>
<dd>{{franvaro.otherKnownReason}}</dd>
</ng-container>
<ng-container *ngIf="franvaro.knownReasonComment">
<dt>Beskrivning för frånvaro</dt>
<dd>{{franvaro.knownReasonComment}}</dd>
</ng-container>
<dt>Dag för frånvaro</dt>
<dd>
<digi-typography-time [afDateTime]="franvaro.date"></digi-typography-time>
</dd>
<dt>Heldag eller del av dag</dt>
<dd>{{franvaro.wholeDay ? 'Heldag' : 'Del av dag'}}</dd>
<ng-container *ngIf="!franvaro.wholeDay">
<dt>Tid för frånvaro</dt>
<dd>{{franvaro.startTime}} - {{franvaro.endTime}}</dd>
</ng-container>
<dt>Tid för förväntad närvaro</dt>
<dd>{{franvaro.expectedPresenceStartTime}} - {{franvaro.expectedPresenceEndTime}}</dd>
</ng-container>
</dl>
<footer class="franvaro-report-view__footer">
<msfa-back-link route="../../">Tillbaka till deltagaren</msfa-back-link>
</footer>
</div>
</msfa-report-layout>
</msfa-layout>
<ng-template #skeletonRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar Avvikelserapport (frånvaro)"></digi-ng-skeleton-base>
</ng-template>
<ng-template #loadingRef>
<msfa-loader type="padded"></msfa-loader>
</ng-template>

View File

@@ -0,0 +1,31 @@
@import 'mixins/list';
@import 'variables/gutters';
.franvaro-report-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);
}
}

View File

@@ -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 { FranvaroReportViewComponent } from './franvaro-report-view.component';
import { FranvaroReportViewService } from './franvaro-report-view.service';
describe('FranvaroReportViewComponent', () => {
let component: FranvaroReportViewComponent;
let fixture: ComponentFixture<FranvaroReportViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [FranvaroReportViewComponent, LayoutComponent],
imports: [RouterTestingModule, HttpClientTestingModule],
providers: [FranvaroReportViewService],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(FranvaroReportViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,37 @@
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 { FranvaroReport } from '@msfa-models/franvaro.model';
import { Observable } from 'rxjs';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import { FranvaroReportViewService } from './franvaro-report-view.service';
@Component({
selector: 'msfa-franvaro-report-view',
templateUrl: './franvaro-report-view.component.html',
styleUrls: ['./franvaro-report-view.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FranvaroReportViewComponent {
params$: Observable<Params> = this.activatedRoute.params.pipe(
map(params => ({
reportId: params.reportId as string,
genomforandeReferens: params.genomforandeReferens as string,
}))
);
avrop$: Observable<Avrop> = this.params$.pipe(
switchMap(({ genomforandeReferens }) =>
this.franvaroReportViewService.fetchAvropInformation$(+genomforandeReferens)
),
shareReplay(1)
);
franvaroReport$: Observable<FranvaroReport> = this.params$.pipe(
switchMap(({ genomforandeReferens, reportId }) =>
this.franvaroReportViewService.fetchFranvaroReport$(+genomforandeReferens, reportId as string)
),
shareReplay(1)
);
constructor(private franvaroReportViewService: FranvaroReportViewService, private activatedRoute: ActivatedRoute) {}
}

View File

@@ -0,0 +1,29 @@
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
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 { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { FranvaroReportViewComponent } from './franvaro-report-view.component';
import { FranvaroReportViewService } from './franvaro-report-view.service';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [FranvaroReportViewComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: FranvaroReportViewComponent }]),
LayoutModule,
ReportLayoutModule,
BackLinkModule,
LoaderModule,
HideTextModule,
DigiNgSkeletonBaseModule,
],
providers: [FranvaroReportViewService],
exports: [FranvaroReportViewComponent],
})
export class FranvaroReportViewModule {}

View File

@@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { FranvaroReport } from '@msfa-models/franvaro.model';
import { ReportApiService } from '@msfa-services/api/report.api.service';
import { Observable } from 'rxjs';
@Injectable()
export class FranvaroReportViewService {
constructor(private reportApiService: ReportApiService) {}
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
return this.reportApiService.fetchAvropInformation$(genomforandeReferens);
}
public fetchFranvaroReport$(genomforandeReferens: number, reportId: string): Observable<FranvaroReport> {
return this.reportApiService.fetchFranvaroReport$(genomforandeReferens, reportId);
}
}

View File

@@ -0,0 +1,37 @@
<msfa-layout>
<msfa-report-layout
*ngIf="avrop$ | async as avrop; else skeletonRef"
reportTitle="Gemensam planering"
[avrop]="avrop"
>
<div class="gemensam-planering-view" *ngIf="gemensamPlanering$ | async as report; else loadingRef">
<dl>
<dt>Deltar arbetssökande på distans?</dt>
<dd>{{report.distance ? 'Ja' : 'Nej'}}</dd>
<dt>Aktiviteter</dt>
<dd>
<ul class="gemensam-planering-view__activity-list">
<li class="gemensam-planering-view__activity-item" *ngFor="let activity of report.activities">
<digi-icon-check-circle
class="msfa__digi-icon gemensam-planering-view__activity-check"
aria-hidden="true"
></digi-icon-check-circle>
{{activity}}
</li>
</ul>
</dd>
</dl>
<footer class="gemensam-planering-view__footer">
<msfa-back-link route="../../">Tillbaka till deltagaren</msfa-back-link>
</footer>
</div>
</msfa-report-layout>
</msfa-layout>
<ng-template #skeletonRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar Gemensam planering"></digi-ng-skeleton-base>
</ng-template>
<ng-template #loadingRef>
<msfa-loader type="padded"></msfa-loader>
</ng-template>

View File

@@ -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);
}
}

View File

@@ -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 { GemensamPlaneringViewComponent } from './gemensam-planering-view.component';
import { GemensamPlaneringViewService } from './gemensam-planering-view.service';
describe('GemensamPlaneringViewComponent', () => {
let component: GemensamPlaneringViewComponent;
let fixture: ComponentFixture<GemensamPlaneringViewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [GemensamPlaneringViewComponent, LayoutComponent],
imports: [RouterTestingModule, HttpClientTestingModule],
providers: [GemensamPlaneringViewService],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(GemensamPlaneringViewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,40 @@
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 { GemensamPlaneringDetailed } from '@msfa-models/gemensam-planering.model';
import { Observable } from 'rxjs';
import { map, shareReplay, switchMap } from 'rxjs/operators';
import { GemensamPlaneringViewService } from './gemensam-planering-view.service';
@Component({
selector: 'msfa-gemensam-planering-view',
templateUrl: './gemensam-planering-view.component.html',
styleUrls: ['./gemensam-planering-view.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GemensamPlaneringViewComponent {
params$: Observable<Params> = this.activatedRoute.params.pipe(
map(params => ({
reportId: params.reportId as string,
genomforandeReferens: params.genomforandeReferens as string,
}))
);
avrop$: Observable<Avrop> = this.params$.pipe(
switchMap(({ genomforandeReferens }) =>
this.gemensamPlaneringViewService.fetchAvropInformation$(+genomforandeReferens)
),
shareReplay(1)
);
gemensamPlanering$: Observable<GemensamPlaneringDetailed> = this.params$.pipe(
switchMap(({ genomforandeReferens, reportId }) =>
this.gemensamPlaneringViewService.fetchGemensamPlanering$(+genomforandeReferens, reportId as string)
),
shareReplay(1)
);
constructor(
private gemensamPlaneringViewService: GemensamPlaneringViewService,
private activatedRoute: ActivatedRoute
) {}
}

View File

@@ -0,0 +1,29 @@
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
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 { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { GemensamPlaneringViewComponent } from './gemensam-planering-view.component';
import { GemensamPlaneringViewService } from './gemensam-planering-view.service';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [GemensamPlaneringViewComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: GemensamPlaneringViewComponent }]),
LayoutModule,
ReportLayoutModule,
BackLinkModule,
LoaderModule,
HideTextModule,
DigiNgSkeletonBaseModule,
],
providers: [GemensamPlaneringViewService],
exports: [GemensamPlaneringViewComponent],
})
export class GemensamPlaneringViewModule {}

View File

@@ -0,0 +1,21 @@
import { Injectable } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { GemensamPlaneringDetailed } from '@msfa-models/gemensam-planering.model';
import { ReportApiService } from '@msfa-services/api/report.api.service';
import { Observable } from 'rxjs';
@Injectable()
export class GemensamPlaneringViewService {
constructor(private reportApiService: ReportApiService) {}
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
return this.reportApiService.fetchAvropInformation$(genomforandeReferens);
}
public fetchGemensamPlanering$(
genomforandeReferens: number,
reportId: string
): Observable<GemensamPlaneringDetailed> {
return this.reportApiService.fetchGemensamPlanering$(genomforandeReferens, reportId);
}
}

View File

@@ -0,0 +1,15 @@
export interface AvvikelseQuestionResponse {
fraga: string;
svar: string;
}
export interface AvvikelseResponse {
avvikelseOrsakskod: string;
frageformular: AvvikelseQuestionResponse[];
rapporteringsdatum: string;
}
export interface AvvikelseReportResponse {
genomforandeReferens: number;
avvikelseAlternativ: AvvikelseResponse;
}

View File

@@ -0,0 +1,20 @@
export interface FranvaroResponse {
avvikelseOrsaksKod: string;
datum: string;
heldag: boolean;
startTid: string;
slutTid: string;
alternativForKandaOrsaker: {
typ: string;
motivering: string;
} | null;
forvantadNarvaro: {
startTid: string;
slutTid: string;
};
}
export interface FranvaroReportResponse {
genomforandeReferens: number;
franvaro: FranvaroResponse;
}

View File

@@ -0,0 +1,5 @@
export interface GemensamPlaneringResponse {
genomforandeReferens: string;
distans: boolean;
aktivitetsIds: number[];
}

View File

@@ -1,29 +1,64 @@
import { FranvaroAlternativ } from './franvaro-alternativ.model';
import { Fraga } from '@msfa-models/fraga.model';
import { AvvikelseResponse } from './api/avvikelse-response.model';
import { FragorForAvvikelser } from './fragor-for-avvikelser.model';
import { FranvaroAlternativ } from './franvaro-alternativ.model';
import { OrsaksKoderAvvikelse } from './orsaks-koder-avvikelse.model';
export interface AvvikelseAlternativ {
export interface AvvikelseQuestion {
question: string;
answer: string;
}
export interface Avvikelse {
reason: string;
questions: AvvikelseQuestion[];
date: Date;
}
export interface AvvikelseReport {
genomforandeReferens: number;
avvikelse: Avvikelse;
}
export interface AvvikelseAlternativ_OLD {
avvikelseorsakskod: string;
frageformular: Fraga[];
rapporteringsdatum: string;
}
export interface Avvikelse {
export interface Avvikelse_OLD {
genomforandeReferens: number;
avvikelseAlternativ?: AvvikelseAlternativ;
franvaro?: FranvaroAlternativ;
avvikelseAlternativ?: AvvikelseAlternativ_OLD;
}
export interface AvvikelseRequestData {
export interface AvvikelseRequestData_OLD {
genomforandeReferens: number;
avvikelseAlternativ: AvvikelseAlternativ;
avvikelseAlternativ: AvvikelseAlternativ_OLD;
}
export interface FranvaroRequestData {
export interface FranvaroRequestData_OLD {
genomforandeReferens: number;
franvaro: FranvaroAlternativ;
}
export function mapAvvikelseRequestDataToAvvikelse(data: AvvikelseRequestData): Avvikelse {
export function mapResponseToAvvikelse(
data: AvvikelseResponse,
reasons: OrsaksKoderAvvikelse[],
questions: FragorForAvvikelser[]
): Avvikelse {
const { avvikelseOrsakskod, rapporteringsdatum, frageformular } = data;
return {
reason: reasons.find(reason => reason.id === avvikelseOrsakskod).name,
questions: frageformular.map(fraga => ({
question: questions.find(question => question.id === fraga.fraga).name,
answer: fraga.svar,
})),
date: new Date(rapporteringsdatum),
};
}
export function mapAvvikelseRequestDataToAvvikelse_OLD(data: AvvikelseRequestData_OLD): Avvikelse_OLD {
return data;
// const { genomforandeReferens, avvikelseAlternativ } = data;
//

View File

@@ -1,3 +1,6 @@
import { FranvaroResponse } from './api/franvaro-response.model';
import { OrsaksKoderFranvaro } from './orsaks-koder-franvaro.model';
export interface Franvaro {
reason: string;
date: Date;
@@ -9,3 +12,30 @@ export interface Franvaro {
otherKnownReason: string;
knownReasonComment: string;
}
export interface FranvaroReport {
genomforandeReferens: number;
franvaro: Franvaro;
}
export function mapResponseToFranvaro(
data: FranvaroResponse,
reasons: OrsaksKoderFranvaro[],
otherKnownReasons: OrsaksKoderFranvaro[]
): Franvaro {
const { avvikelseOrsaksKod, datum, heldag, startTid, slutTid, alternativForKandaOrsaker, forvantadNarvaro } = data;
return {
reason: reasons.find(reason => reason.value.toString() === avvikelseOrsaksKod).name,
date: new Date(datum),
wholeDay: heldag,
startTime: startTid,
endTime: slutTid,
otherKnownReason: alternativForKandaOrsaker
? otherKnownReasons.find(reason => reason.value.toString() === alternativForKandaOrsaker.typ).name
: null,
knownReasonComment: alternativForKandaOrsaker?.motivering || null,
expectedPresenceStartTime: forvantadNarvaro.startTid,
expectedPresenceEndTime: forvantadNarvaro.slutTid,
};
}

View File

@@ -1,4 +1,6 @@
import { Activity } from './activity.model';
import { GemensamPlaneringPostRequest } from './api/gemensam-planering.request.model';
import { GemensamPlaneringResponse } from './api/gemensam-planering.response.model';
export interface GemensamPlanering {
genomforandeReferens: number;
@@ -6,6 +8,12 @@ export interface GemensamPlanering {
activityIds: number[];
}
export interface GemensamPlaneringDetailed {
genomforandeReferens: number;
distance: boolean;
activities: string[];
}
export function mapGemensamPlaneringToGemensamPlaneringPostRequest(
gemensamPlanering: GemensamPlanering
): GemensamPlaneringPostRequest {
@@ -17,3 +25,16 @@ export function mapGemensamPlaneringToGemensamPlaneringPostRequest(
aktivitetsIds: activityIds,
};
}
export function mapResponseToGemensamPlaneringDetailed(
data: GemensamPlaneringResponse,
activities: Activity[]
): GemensamPlaneringDetailed {
const { genomforandeReferens, distans, aktivitetsIds } = data;
return {
genomforandeReferens: +genomforandeReferens,
distance: distans,
activities: aktivitetsIds.map(id => activities.find(activity => activity.id === id).name),
};
}

View File

@@ -1,5 +1,6 @@
import { ReportType } from '@msfa-enums/report-type.enum';
import { ReportResponse } from './api/report.response.model';
import { Franvaro } from './franvaro.model';
import { PaginationMeta } from './pagination-meta.model';
export interface Report {
@@ -11,6 +12,11 @@ export interface Report {
ciamUserId: string;
}
export interface ReportDetail {
genomforandeReferens: string;
franvaro?: Franvaro;
}
export interface ReportsData {
data: Report[];
meta: PaginationMeta;

View File

@@ -1,10 +1,12 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ErrorType } from '@msfa-enums/error-type.enum';
import { environment } from '@msfa-environment';
import { FragorForAvvikelserResponse } from '@msfa-models/api/fragor-for-avvikelser.response';
import { KandaAvvikelseKoderResponse } from '@msfa-models/api/kanda-avvikelse-koder.response.model';
import { OrsaksKoderAvvikelseResponse } from '@msfa-models/api/orsaks-koder-avvikelse.response.model';
import { Avvikelse } from '@msfa-models/avvikelse.model';
import { Avvikelse_OLD } from '@msfa-models/avvikelse.model';
import { CustomError } from '@msfa-models/error/custom-error';
import { FragorForAvvikelser, mapResponseToFragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model';
import { mapResponseToOrsaksKoderAvvikelse, OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model';
import {
@@ -15,8 +17,6 @@ import {
} from '@msfa-models/orsaks-koder-franvaro.model';
import { Observable } from 'rxjs';
import { catchError, filter, map } from 'rxjs/operators';
import { CustomError } from '@msfa-models/error/custom-error';
import { ErrorType } from '@msfa-enums/error-type.enum';
@Injectable({
providedIn: 'root',
@@ -58,7 +58,7 @@ export class AvvikelseApiService {
);
}
public createAvvikelse$(avvikelse: Avvikelse): Observable<unknown> {
public createAvvikelse$(avvikelse: Avvikelse_OLD): Observable<unknown> {
return this.httpClient.post<void>(`${this._apiBaseUrl}/avvikelse`, avvikelse).pipe(
catchError((error: Error) => {
throw new CustomError({
@@ -70,7 +70,7 @@ export class AvvikelseApiService {
);
}
public createFranvaro$(avvikelse: Avvikelse): Promise<void> {
public createFranvaro$(avvikelse: Avvikelse_OLD): Promise<void> {
return this.httpClient.post<void>(`${this._apiBaseUrl}/franvaro`, avvikelse).toPromise();
}
}

View File

@@ -3,7 +3,7 @@ import { Injectable } from '@angular/core';
import { environment } from '@msfa-environment';
import { OrsaksKoderFranvaroResponse } from '@msfa-models/api/orsaks-koder-franvaro.response.model';
import { Avrop } from '@msfa-models/avrop.model';
import { FranvaroRequestData } from '@msfa-models/avvikelse.model';
import { FranvaroRequestData_OLD } from '@msfa-models/avvikelse.model';
import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
import { mapResponseToOrsaksKoderFranvaro, OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
@@ -47,7 +47,7 @@ export class FranvaroReportApiService {
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
}
public async postFranvaroReport$(requestData: FranvaroRequestData): Promise<void> {
public async postFranvaroReport$(requestData: FranvaroRequestData_OLD): Promise<void> {
return this.httpClient.post<void>(`${this._apiBaseUrl}/franvaro`, requestData).toPromise();
}
}

View File

@@ -4,17 +4,24 @@ import { environment } from '@msfa-environment';
import { Activity, mapResponseToActivity } from '@msfa-models/activity.model';
import { ActivityResponse } from '@msfa-models/api/activity.response.model';
import { GemensamPlaneringPostRequest } from '@msfa-models/api/gemensam-planering.request.model';
import { GemensamPlaneringResponse } from '@msfa-models/api/gemensam-planering.response.model';
import { Params } from '@msfa-models/api/params.model';
import { Avrop } from '@msfa-models/avrop.model';
import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
import {
GemensamPlaneringDetailed,
mapResponseToGemensamPlaneringDetailed,
} from '@msfa-models/gemensam-planering.model';
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
import { Observable } from 'rxjs';
import { catchError, filter, map, shareReplay } from 'rxjs/operators';
import { catchError, filter, map, shareReplay, switchMap } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
})
export class GemensamPlaneringApiService {
private _apiBaseUrl = `${environment.api.url}/rapporter/gemensam-planering`;
private _apiHandlingarUrl = `${environment.api.url}/handlingar`;
public fetchActivities$(): Observable<Activity[]> {
return this.httpClient.get<{ data: ActivityResponse[] }>(`${this._apiBaseUrl}/aktiviteter`).pipe(
@@ -33,6 +40,19 @@ export class GemensamPlaneringApiService {
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
}
public fetchReport$(genomforandeReferens: number, reportId: string): Observable<GemensamPlaneringDetailed> {
const params: Params = {
genomforandereferens: genomforandeReferens.toString(),
};
return this.fetchActivities$().pipe(
switchMap(activities =>
this.httpClient
.get<{ data: GemensamPlaneringResponse }>(`${this._apiHandlingarUrl}/${reportId}`, { params })
.pipe(map(({ data }) => mapResponseToGemensamPlaneringDetailed(data, activities)))
)
);
}
public async postGemensamPlanering$(requestData: GemensamPlaneringPostRequest): Promise<void> {
return this.httpClient.post<void>(`${this._apiBaseUrl}`, requestData).toPromise();
}

View File

@@ -0,0 +1,107 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@msfa-environment';
import { Activity } from '@msfa-models/activity.model';
import { AvvikelseReportResponse } from '@msfa-models/api/avvikelse-response.model';
import { FranvaroReportResponse } from '@msfa-models/api/franvaro-response.model';
import { GemensamPlaneringResponse } from '@msfa-models/api/gemensam-planering.response.model';
import { Params } from '@msfa-models/api/params.model';
import { Avrop } from '@msfa-models/avrop.model';
import { AvvikelseReport, mapResponseToAvvikelse } from '@msfa-models/avvikelse.model';
import { FragorForAvvikelser } from '@msfa-models/fragor-for-avvikelser.model';
import { FranvaroReport, mapResponseToFranvaro } from '@msfa-models/franvaro.model';
import {
GemensamPlaneringDetailed,
mapResponseToGemensamPlaneringDetailed,
} from '@msfa-models/gemensam-planering.model';
import { OrsaksKoderAvvikelse } from '@msfa-models/orsaks-koder-avvikelse.model';
import { OrsaksKoderFranvaro } from '@msfa-models/orsaks-koder-franvaro.model';
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
import { combineLatest, Observable } from 'rxjs';
import { filter, map, switchMap } from 'rxjs/operators';
import { AvvikelseApiService } from './avvikelse-api.service';
import { FranvaroReportApiService } from './franvaro-report.api.service';
import { GemensamPlaneringApiService } from './gemensam-planering-api.service';
@Injectable({
providedIn: 'root',
})
export class ReportApiService {
private _apiBaseUrl = `${environment.api.url}/handlingar`;
private _avvikelseReasons$: Observable<OrsaksKoderAvvikelse[]> = this.avvikelseApiService.getOrsaksKoderAvvikelse$();
private _avvikelseQuestions$: Observable<FragorForAvvikelser[]> = this.avvikelseApiService.getFragorForAvvikelser$();
private _activities$: Observable<Activity[]> = this.gemensamPlaneringApiService.fetchActivities$();
private _franvaroReasons$: Observable<OrsaksKoderFranvaro[]> = this.franvaroReportApiService.fetchReasons$();
private _otherFranvaroReasons$: Observable<
OrsaksKoderFranvaro[]
> = this.franvaroReportApiService.fetchOtherKnownReasons$();
constructor(
private httpClient: HttpClient,
private deltagareApiService: DeltagareApiService,
private gemensamPlaneringApiService: GemensamPlaneringApiService,
private franvaroReportApiService: FranvaroReportApiService,
private avvikelseApiService: AvvikelseApiService
) {}
public fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
}
public fetchGemensamPlanering$(
genomforandeReferens: number,
reportId: string
): Observable<GemensamPlaneringDetailed> {
const params: Params = {
genomforandereferens: genomforandeReferens.toString(),
};
return this._activities$.pipe(
switchMap(activities =>
this.httpClient
.get<{ data: GemensamPlaneringResponse }>(`${this._apiBaseUrl}/${reportId}`, { params })
.pipe(map(({ data }) => mapResponseToGemensamPlaneringDetailed(data, activities)))
)
);
}
public fetchFranvaroReport$(genomforandeReferens: number, reportId: string): Observable<FranvaroReport> {
const params: Params = {
genomforandereferens: genomforandeReferens.toString(),
};
return combineLatest([this._franvaroReasons$, this._otherFranvaroReasons$]).pipe(
filter(([reasons, otherReasons]) => !!(reasons && otherReasons)),
switchMap(([reasons, otherReasons]) => {
return this.httpClient
.get<{ data: FranvaroReportResponse }>(`${this._apiBaseUrl}/${reportId}`, { params })
.pipe(
map(({ data }) => ({
genomforandeReferens: data.genomforandeReferens,
franvaro: mapResponseToFranvaro(data.franvaro, reasons, otherReasons),
}))
);
})
);
}
public fetchAvvikelseReport$(genomforandeReferens: number, reportId: string): Observable<AvvikelseReport> {
const params: Params = {
genomforandereferens: genomforandeReferens.toString(),
};
return combineLatest([this._avvikelseReasons$, this._avvikelseQuestions$]).pipe(
filter(([reasons, questions]) => !!(reasons && questions)),
switchMap(([reasons, questions]) => {
return this.httpClient
.get<{ data: AvvikelseReportResponse }>(`${this._apiBaseUrl}/${reportId}`, { params })
.pipe(
map(({ data }) => ({
genomforandeReferens: data.genomforandeReferens,
avvikelse: mapResponseToAvvikelse(data.avvikelseAlternativ, reasons, questions),
}))
);
})
);
}
}

View File

@@ -6,7 +6,7 @@ export function mapPathsToBreadcrumbs(
paths: string[],
startBreadcrumb?: NavigationBreadcrumbsItem
): NavigationBreadcrumbsItem[] {
const breadcrumbs = [
let breadcrumbs = [
...(startBreadcrumb ? [startBreadcrumb] : []),
...paths.map((path, index) => ({
text: mapPathToPageName(path),
@@ -24,7 +24,11 @@ export function mapPathsToBreadcrumbs(
breadcrumbs[breadcrumbs.length - 1].text = 'Redigera personalkonto';
} else if (isDeltagareCardRoute(paths)) {
breadcrumbs[breadcrumbs.length - 1].text = 'Deltagarinformation';
} else if (isDeltagareReportingFormRoute(paths)) {
breadcrumbs[breadcrumbs.length - 2].text = 'Deltagarinformation';
breadcrumbs[breadcrumbs.length - 1].text = `Skapa ${DELTAGARE_REPORTING_ROUTES[paths[paths.length - 1]] as string}`;
} else if (isDeltagareReportingRoute(paths)) {
breadcrumbs = breadcrumbs.slice(0, -1);
breadcrumbs[breadcrumbs.length - 2].text = 'Deltagarinformation';
}
@@ -43,6 +47,10 @@ function isDeltagareCardRoute(paths: string[]): boolean {
return paths.length === 2 && paths[0] === 'deltagare';
}
function isDeltagareReportingRoute(paths: string[]): boolean {
function isDeltagareReportingFormRoute(paths: string[]): boolean {
return paths.length === 3 && paths[0] === 'deltagare' && paths[2] in DELTAGARE_REPORTING_ROUTES;
}
function isDeltagareReportingRoute(paths: string[]): boolean {
return paths.length === 4 && paths[0] === 'deltagare' && paths[2] in DELTAGARE_REPORTING_ROUTES;
}

View File

@@ -152,21 +152,21 @@ pipeline {
}
// Temporary while data in sys is so bad
stage('Deploy to "test"') {
steps {
echo '### Deploying to "test"... ###'
script {
openshift.withCluster() {
openshift.withProject(test_project) {
openshift.raw("set image dc/${ appname } ${ appname }=docker-registry.default.svc:5000/${utv_project}/${ appname }:${BUILD_TAG} --record=true --source=docker")
openshift.raw("annotate dc ${ appname } version=${BUILD_TAG} --overwrite=true")
openshift.selector("dc", "${ appname }").rollout().status();
}
}
}
echo '### Deployed to "test"! ###'
}
}
// stage('Deploy to "test"') {
// steps {
// echo '### Deploying to "test"... ###'
// script {
// openshift.withCluster() {
// openshift.withProject(test_project) {
// openshift.raw("set image dc/${ appname } ${ appname }=docker-registry.default.svc:5000/${utv_project}/${ appname }:${BUILD_TAG} --record=true --source=docker")
// openshift.raw("annotate dc ${ appname } version=${BUILD_TAG} --overwrite=true")
// openshift.selector("dc", "${ appname }").rollout().status();
// }
// }
// }
// echo '### Deployed to "test"! ###'
// }
// }
}
}