feat(reports): Added warnings when avrop is past end-date and reporting should be disabled. (TV-811)

Squashed commit of the following:

commit 2b42a23e3fc1599375a6b321c89eb3cc1cf1baa5
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 25 10:03:29 2021 +0200

    Typo

commit 9534faccaeca0b5b4224715dbc5984c50e471a13
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 25 09:04:52 2021 +0200

    Implemented warning for GP

commit 585fa6c8abfa60604a38d443275ad8facf0ba256
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 25 07:35:40 2021 +0200

    Implemented warning for avvikelsereport

commit 37c45f89c6c11fa1d7357de0e9b1b626312bf0de
Merge: 1276f606 612e0997
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Mon Oct 25 07:26:35 2021 +0200

    Merge branch 'develop' into feature/TV-811-report-warnings-when-avrop-is-past-enddate

commit 1276f606ddf9f1393c57c16a44144b8431ddf015
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Fri Oct 22 15:08:41 2021 +0200

    Implemented warning message for frånvaro
This commit is contained in:
Erik Tiekstra
2021-10-25 12:06:28 +02:00
parent 612e099737
commit fca91fbbf1
8 changed files with 199 additions and 120 deletions

View File

@@ -6,117 +6,128 @@
reportTitle="Avvikelserapport (avvikelse)"
>
<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="avvikelse-report-form__alert"
>
<p>Avvikelserapport (avvikelse) 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>
<div class="avvikelse-report-form__warning" *ngIf="!isAllowedToReport(avrop); else reportRef">
<digi-notification-alert af-variation="warning" af-heading="Kan inte skapa Avvikelserapport (avvikelse)">
<p>{{notAllowedToReportWarning(avrop)}}</p>
</digi-notification-alert>
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>
</div>
<ng-template #formRef>
<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"
afLabel="Orsak till avvikelse"
afPlaceholder="Välj orsak till avvikelse"
[afSelectItems]="reason"
[afRequired]="true"
[afInvalidMessage]="reasonFormControl.errors?.required"
[afAnnounceIfOptional]="true"
[afDisableValidStyle]="true"
[afInvalid]="formControlIsInvalid(reasonFormControl)"
></digi-ng-form-select>
</div>
<div
class="avvikelse-report-form__form-item avvikelse-report-form__textareas"
[formArrayName]="questionsFormName"
*ngIf="questionsForChosenReason$ | async; let questions"
>
<div
class="avvikelse-report-form__form-item"
*ngFor="let question of questionsFormArray.controls; let i=index"
>
<digi-ng-form-textarea
[formControlName]="i"
[afLabel]="questions[i]?.name"
[afDisableValidStyle]="true"
[afRequired]="questionIsRequired(questions[i])"
[afInvalidMessage]="question.errors?.required || question.errors?.invalid"
[afAnnounceIfOptional]="true"
[afMaxLength]="2000"
[afInvalid]="formControlIsInvalid(question)"
></digi-ng-form-textarea>
</div>
</div>
<div class="avvikelse-report-form__form-item" *ngIf="chosenReasonId$ | async">
<digi-ng-form-datepicker
[afDisableValidStyle]="true"
[afMinDate]="minDate(avrop)"
[afMaxDate]="maxDate"
[afInvalid]="formControlIsInvalid(avvikelseDateFormControl)"
[afValidationMessages]="{required: 'Datum är obligatoriskt'}"
[afAnnounceIfOptional]="true"
[afRequired]="true"
[afLabel]="'Välj dag för avvikelse'"
[formControlName]="reportingDateFormName"
></digi-ng-form-datepicker>
</div>
<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>
<span class="msfa__a11y-sr-only">&nbsp;och gå tillbaka till deltagaren</span>
</msfa-back-link>
</div>
</form>
<digi-ng-dialog
[afActive]="confirmDialogIsOpen$ | async"
(afOnPrimaryClick)="submitAndCloseConfirmDialog()"
(afOnInactive)="cancelConfirmDialog()"
afHeadingLevel="h2"
afPrimaryButtonText="Skicka in"
afSecondaryButtonText="Avbryt"
(afOnSecondaryClick)="cancelConfirmDialog()"
afHeading="Vill du skicka in Avvikelserapport (avvikelse)"
afAriaLabel="Förhandsgranska och skicka in Avvikelserapport (avvikelse)"
id="confirmAvvikelserapport"
>
<msfa-loader *ngIf="submitIsLoading$ | async" type="absolute"></msfa-loader>
<msfa-report-description-list [avrop]="avrop">
<dt>Orsak till avvikelse:</dt>
<dd>{{(chosenReason$ | async)?.name }}</dd>
<ng-container *ngIf="avvikelseSubmitData$ | async; let avvikelseSubmitData; else loadingRef">
<ng-container *ngFor="let question of avvikelseSubmitData.avvikelseAlternativ.frageformular">
<dt>{{getCurrentQuestionFromId(question.fraga).name}}</dt>
<dd>{{question.svar.length === 0 ? 'Inget svar' : question.svar }}</dd>
</ng-container>
<dt>Dag för avvikelse:</dt>
<dd>{{avvikelseSubmitData.avvikelseAlternativ.rapporteringsdatum }}</dd>
</ng-container>
</msfa-report-description-list>
<ng-template #reportRef>
<div *ngIf="submittedDate$ | async as submittedDate; else formRef" class="avvikelse-report-form__confirmation">
<digi-notification-alert
*ngIf="submitError$ | async as error"
af-variation="danger"
af-heading="Någonting gick fel"
af-heading="Allt gick bra"
af-heading-level="h3"
af-variation="success"
class="avvikelse-report-form__alert"
>
<p>Kunde inte spara Avvikelserapport (avvikelse). Ladda om sidan och försök igen.</p>
<p *ngIf="error.message" class="msfa__small-text">{{error.message}}</p>
<p>
Avvikelserapport (avvikelse) 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>
</digi-ng-dialog>
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>
</div>
<ng-template #formRef>
<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"
afLabel="Orsak till avvikelse"
afPlaceholder="Välj orsak till avvikelse"
[afSelectItems]="reason"
[afRequired]="true"
[afInvalidMessage]="reasonFormControl.errors?.required"
[afAnnounceIfOptional]="true"
[afDisableValidStyle]="true"
[afInvalid]="formControlIsInvalid(reasonFormControl)"
></digi-ng-form-select>
</div>
<div
class="avvikelse-report-form__form-item avvikelse-report-form__textareas"
[formArrayName]="questionsFormName"
*ngIf="questionsForChosenReason$ | async; let questions"
>
<div
class="avvikelse-report-form__form-item"
*ngFor="let question of questionsFormArray.controls; let i=index"
>
<digi-ng-form-textarea
[formControlName]="i"
[afLabel]="questions[i]?.name"
[afDisableValidStyle]="true"
[afRequired]="questionIsRequired(questions[i])"
[afInvalidMessage]="question.errors?.required || question.errors?.invalid"
[afAnnounceIfOptional]="true"
[afMaxLength]="2000"
[afInvalid]="formControlIsInvalid(question)"
></digi-ng-form-textarea>
</div>
</div>
<div class="avvikelse-report-form__form-item" *ngIf="chosenReasonId$ | async">
<digi-ng-form-datepicker
[afDisableValidStyle]="true"
[afMinDate]="avrop.receivedTimestamp"
[afMaxDate]="maxDate(avrop.endDate)"
[afInvalid]="formControlIsInvalid(avvikelseDateFormControl)"
[afValidationMessages]="{required: 'Datum är obligatoriskt'}"
[afAnnounceIfOptional]="true"
[afRequired]="true"
[afLabel]="'Välj dag för avvikelse'"
[formControlName]="reportingDateFormName"
></digi-ng-form-datepicker>
</div>
<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>
<span class="msfa__a11y-sr-only">&nbsp;och gå tillbaka till deltagaren</span>
</msfa-back-link>
</div>
</form>
<digi-ng-dialog
[afActive]="confirmDialogIsOpen$ | async"
(afOnPrimaryClick)="submitAndCloseConfirmDialog()"
(afOnInactive)="cancelConfirmDialog()"
afHeadingLevel="h2"
afPrimaryButtonText="Skicka in"
afSecondaryButtonText="Avbryt"
(afOnSecondaryClick)="cancelConfirmDialog()"
afHeading="Vill du skicka in Avvikelserapport (avvikelse)"
afAriaLabel="Förhandsgranska och skicka in Avvikelserapport (avvikelse)"
id="confirmAvvikelserapport"
>
<msfa-loader *ngIf="submitIsLoading$ | async" type="absolute"></msfa-loader>
<msfa-report-description-list [avrop]="avrop">
<dt>Orsak till avvikelse:</dt>
<dd>{{(chosenReason$ | async)?.name }}</dd>
<ng-container *ngIf="avvikelseSubmitData$ | async; let avvikelseSubmitData; else loadingRef">
<ng-container *ngFor="let question of avvikelseSubmitData.avvikelseAlternativ.frageformular">
<dt>{{getCurrentQuestionFromId(question.fraga).name}}</dt>
<dd>{{question.svar.length === 0 ? 'Inget svar' : question.svar }}</dd>
</ng-container>
<dt>Dag för avvikelse:</dt>
<dd>{{avvikelseSubmitData.avvikelseAlternativ.rapporteringsdatum }}</dd>
</ng-container>
</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 Avvikelserapport (avvikelse). Ladda om sidan och försök igen.</p>
<p *ngIf="error.message" class="msfa__small-text">{{error.message}}</p>
</digi-notification-alert>
</digi-ng-dialog>
</ng-template>
</ng-template>
</div>
</msfa-report-layout>

View File

@@ -5,6 +5,7 @@
&__confirmation,
&__textareas,
&__warning,
&__form {
display: flex;
flex-direction: column;

View File

@@ -10,6 +10,7 @@ import { CustomError } from '@msfa-models/error/custom-error';
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 { addDays } from 'date-fns';
import { BehaviorSubject, combineLatest, Observable, Subscription } from 'rxjs';
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
import { AvvikelseReportFormService } from './avvikelse-report-form.service';
@@ -87,10 +88,6 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
return this.avvikelseFormGroup.get('questions') as FormArray;
}
get maxDate(): Date {
return new Date();
}
getCurrentQuestionFromId(id: string): AvvikelseQuestion {
return this.currentQuestions.find(currentQuestions => currentQuestions.id === id);
}
@@ -133,8 +130,30 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
}
minDate(avrop: Avrop): Date {
return new Date(avrop.receivedTimestamp);
maxDate(endDate: Date): Date {
const now = new Date();
return now < endDate ? now : endDate;
}
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 Avvikelserapport (avvikelse) eftersom tjänsten har avslutats.';
}
if (!this._isAfterStartDate(avrop.startDate)) {
return 'Det går inte att göra Avvikelserapport (avvikelse) eftersom tjänsten inte har startat ännu.';
}
}
openConfirmDialog(): void {

View File

@@ -6,9 +6,9 @@
reportTitle="Avvikelserapport (frånvaro)"
>
<div class="franvaro-report-form" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
<div class="franvaro-report-form__warning" *ngIf="maxDate < avrop.startDate; else reportRef">
<div class="franvaro-report-form__warning" *ngIf="!isAllowedToReport(avrop); 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>
<p>{{notAllowedToReportWarning(avrop)}}</p>
</digi-notification-alert>
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>
@@ -112,7 +112,7 @@
<digi-ng-form-datepicker
[afDisableValidStyle]="true"
[afMinDate]="avrop.startDate"
[afMaxDate]="maxDate"
[afMaxDate]="maxDate(avrop.endDate)"
[afInvalid]="formControlIsInvalid(['date'])"
[afAnnounceIfOptional]="true"
[afRequired]="true"

View File

@@ -9,6 +9,7 @@ import { CustomError } from '@msfa-models/error/custom-error';
import { FranvaroReason } from '@msfa-models/franvaro-reason.model';
import { Franvaro } from '@msfa-models/franvaro.model';
import { formatDate } from '@msfa-utils/format-to-date.util';
import { addDays } from 'date-fns';
import { BehaviorSubject, Observable } from 'rxjs';
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
import { FranvaroReportFormService } from './franvaro-report-form.service';
@@ -21,7 +22,6 @@ import { FranvaroReportFormValidator } from './franvaro-report-form.validator';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FranvaroReportFormComponent {
maxDate = new Date();
shouldValidate$ = new BehaviorSubject<boolean>(false);
confirmDialogOpen$ = new BehaviorSubject<boolean>(false);
franvaroFormGroup = new FormGroup(
@@ -117,12 +117,38 @@ export class FranvaroReportFormComponent {
return new Date(this.dateFormControl.value);
}
get dayOrPartOfDayFromValue(): string {
return this.wholeDayFormControl.value ? 'Heldag' : 'Del av dag';
}
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 Avvikelserapport (frånvaro) eftersom tjänsten har avslutats.';
}
if (!this._isAfterStartDate(avrop.startDate)) {
return 'Det går inte att göra Avvikelserapport (frånvaro) eftersom tjänsten inte har startat ännu.';
}
}
getReasonNameFromValue(reasons: FranvaroReason[], value: string): string {
return reasons.find(reason => reason.value.toString() === value)?.name;
}
get dayOrPartOfDayFromValue(): string {
return this.wholeDayFormControl.value ? 'Heldag' : 'Del av dag';
maxDate(endDate: Date): Date {
const now = new Date();
return now < endDate ? now : endDate;
}
formControlIsInvalid(formControlNames: string[]): boolean {

View File

@@ -5,9 +5,9 @@
[avrop]="avrop"
>
<div class="gemensam-planering-form" *ngIf="currentGenomforandeReferens$ | async as genomforandeReferens">
<div class="gemensam-planering-form__warning" *ngIf="today < avrop.startDate; else reportRef">
<div class="gemensam-planering-form__warning" *ngIf="!isAllowedToReport(avrop); 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>
<p>{{notAllowedToReportWarning(avrop)}}</p>
</digi-notification-alert>
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>

View File

@@ -9,6 +9,7 @@ import {
GemensamPlanering,
mapGemensamPlaneringToGemensamPlaneringPostRequest,
} from '@msfa-models/gemensam-planering.model';
import { addDays } from 'date-fns';
import { BehaviorSubject, Observable } from 'rxjs';
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
import { GemensamPlaneringFormService } from './gemensam-planering-form.service';
@@ -147,4 +148,25 @@ export class GemensamPlaneringFormComponent {
},
});
}
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 Gemensam planering eftersom tjänsten har avslutats.';
}
if (!this._isAfterStartDate(avrop.startDate)) {
return 'Det går inte att göra Gemensam planering eftersom tjänsten inte har startat ännu.';
}
}
}

View File

@@ -72,6 +72,6 @@ export function mapAvropResponseToAvrop(data: AvropResponse): Avrop {
utforandeVerksamhet: utforandeverksamhet,
handledareCiamUserId: handledareCiamUserId,
handledare,
receivedTimestamp: recievedTimestamp,
receivedTimestamp: new Date(recievedTimestamp),
};
}