feature(Periodisk redovisning): Formulär för periodisk redovisning (TV-771)
Squashed commit of the following: commit eee14a464fe2fe2a99074f0fe92eecfc92cd05fa Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Oct 20 14:09:48 2021 +0200 styling commit b95bac31ac2b33b5c383a32f06ababf3e5f00245 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Oct 20 13:35:03 2021 +0200 Update periodisk-redovisning.validator.ts commit aeda04cd6705e72b5621a3079904617322ce3036 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Oct 20 13:34:15 2021 +0200 Deltagaren har inte deltagit i några aktiviteter denna period checkbox commit f6ee1ff62d5001e8319bfff04ceb6950ebce9cff Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Oct 20 11:12:57 2021 +0200 form validation and dialog done commit 93e5345d13caf5ab25dc581d58efe92f85acb2dd Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Oct 20 09:16:03 2021 +0200 hidden checkboxes commit 68c2f17ec8417ce5a0404d5b0c00e4800b738143 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Wed Oct 20 08:19:01 2021 +0200 Update app.module.ts commit 2a1dfa6559b9b86839de8ddd1d8cd7c821a56b3a Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Tue Oct 19 21:56:22 2021 +0200 form array with checkboxes done commit 32f26800656d13d1c6c30b20c8187b20fda3c71c Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Tue Oct 19 17:04:33 2021 +0200 activity form array commit db2974cfcca453390ebb4f637daf9d9064b527da Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Tue Oct 19 15:56:39 2021 +0200 add radiobuttons commit 2c4099b48337aaad1cb5b0cc4794ee94e6bb508c Merge: 1ae24a9025b12092Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Tue Oct 19 14:07:46 2021 +0200 Merge branch 'develop' into feature/TV-771-periodisk-redovisning commit 1ae24a905a6c915dcc7d5e3b0cf77a8b62b44d7c Merge: 79e0cf39 794bbc9a Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Oct 19 10:14:29 2021 +0200 Merge branch 'feature/TV-771-periodisk-redovisning' of ssh://bitbucket.arbetsformedlingen.se:7999/tea/mina-sidor-fa-web into feature/TV-771-periodisk-redovisning commit 79e0cf394055527ba09f0d1ae97ddc7c519f2236 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Oct 19 10:14:18 2021 +0200 Updated periods commit 794bbc9a71a0e638196d961ed8b3093de5a64e49 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Tue Oct 19 09:30:24 2021 +0200 Update package-lock.json commit 56351afb1f92060b9f743233a69a785114a3ee96 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Mon Oct 18 17:03:19 2021 +0200 Update periodisk-redovisning-form.component.ts commit 213e6c888a8e388381cf4370d2f5020987b29c4f Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Mon Oct 18 17:01:17 2021 +0200 Update extract-avrop-periods.ts commit 4bcd9669b70070654111f650e6b20d8d8981b3a1 Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Mon Oct 18 17:00:47 2021 +0200 avrop periods commit cee788517c34107a2f651313038c343bb4fc702e Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Mon Oct 18 15:46:48 2021 +0200 clean up commit 3d1d2414270a0de1111ba8b16194dc82ec5bbe79 Merge: b6304eed9104fc31Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se> Date: Mon Oct 18 15:37:08 2021 +0200 Merge branch 'develop' into feature/periodisk-redovisning commit b6304eedf683ba9679e38628d9d3cc33c07103a7 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Oct 15 14:37:59 2021 +0200 Added testdata to test around with inside the component commit d036a771e9139ed6523f71078fa0cb76b936c88b Merge: cb2198415d2f63b9Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Fri Oct 15 14:03:25 2021 +0200 Merge branch 'develop' into feature/periodisk-redovisning ... and 3 more commits
This commit is contained in:
@@ -1,30 +1,37 @@
|
|||||||
import { registerLocaleData } from '@angular/common';
|
import { registerLocaleData } from '@angular/common';
|
||||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||||
import localeSe from '@angular/common/locales/sv';
|
import localeSe from '@angular/common/locales/sv';
|
||||||
import { ErrorHandler, LOCALE_ID, NgModule } from '@angular/core';
|
import { ErrorHandler, LOCALE_ID, NgModule, Provider } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { ApmErrorHandler } from '@elastic/apm-rum-angular';
|
import { ApmErrorHandler } from '@elastic/apm-rum-angular';
|
||||||
|
import { environment } from '@msfa-environment';
|
||||||
import { AuthInterceptor } from '@msfa-interceptors/auth.interceptor';
|
import { AuthInterceptor } from '@msfa-interceptors/auth.interceptor';
|
||||||
import { CustomErrorHandler } from '@msfa-interceptors/custom-error-handler';
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { ToastListModule } from './components/toast-list/toast-list.module';
|
import { ToastListModule } from './components/toast-list/toast-list.module';
|
||||||
import { LoggingModule } from './logging.module';
|
import { LoggingModule } from './logging.module';
|
||||||
import { AvropModule } from './pages/avrop/avrop.module';
|
import { AvropModule } from './pages/avrop/avrop.module';
|
||||||
|
import { CustomErrorHandler } from '@msfa-interceptors/custom-error-handler';
|
||||||
|
|
||||||
registerLocaleData(localeSe);
|
registerLocaleData(localeSe);
|
||||||
|
const providers: Provider[] = [
|
||||||
|
ApmErrorHandler,
|
||||||
|
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||||
|
{ provide: LOCALE_ID, useValue: 'sv-SE' },
|
||||||
|
];
|
||||||
|
// Skip error handler in Dev until "Uncaught Error: ApplicationRef.tick is called recursively" is fixed
|
||||||
|
if (environment.production) {
|
||||||
|
providers.push({
|
||||||
|
provide: ErrorHandler,
|
||||||
|
useClass: CustomErrorHandler,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent],
|
declarations: [AppComponent],
|
||||||
imports: [LoggingModule, BrowserModule, HttpClientModule, AppRoutingModule, ToastListModule, AvropModule],
|
imports: [LoggingModule, BrowserModule, HttpClientModule, AppRoutingModule, ToastListModule, AvropModule],
|
||||||
providers: [
|
providers,
|
||||||
ApmErrorHandler,
|
|
||||||
{
|
|
||||||
provide: ErrorHandler,
|
|
||||||
useClass: CustomErrorHandler,
|
|
||||||
},
|
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
|
||||||
{ provide: LOCALE_ID, useValue: 'sv-SE' },
|
|
||||||
],
|
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
import { Feature } from '@msfa-enums/feature.enum';
|
||||||
|
import { environment } from '@msfa-environment';
|
||||||
|
|
||||||
|
const activeFeatures: Feature[] = environment.activeFeatures;
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@@ -55,21 +59,32 @@ const routes: Routes = [
|
|||||||
m => m.GemensamPlaneringViewModule
|
m => m.GemensamPlaneringViewModule
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'periodisk-redovisning',
|
|
||||||
data: { title: 'Skapa periodisk redovisning' },
|
|
||||||
loadChildren: () =>
|
|
||||||
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/signal-form/signal-form.module').then(m => m.SignalFormModule),
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
activeFeatures.forEach(feature => {
|
||||||
|
switch (feature) {
|
||||||
|
case Feature.REPORTING_PERIODISK_REDOVISNING:
|
||||||
|
routes.push({
|
||||||
|
path: 'periodisk-redovisning',
|
||||||
|
data: { title: 'Skapa Periodisk redovisning' },
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./pages/report-forms/periodisk-redovisning-form/periodisk-redovisning-form.module').then(
|
||||||
|
m => m.PeriodiskRedovisningFormModule
|
||||||
|
),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case Feature.REPORTING_SIGNAL:
|
||||||
|
routes.push({
|
||||||
|
path: 'signal',
|
||||||
|
data: { title: 'Skapa signal om arbete eller studier' },
|
||||||
|
loadChildren: () => import('./pages/report-forms/signal-form/signal-form.module').then(m => m.SignalFormModule),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forChild(routes)],
|
imports: [RouterModule.forChild(routes)],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,7 +24,14 @@
|
|||||||
afText="Skapa ny Avvikelserapport (avvikelse)"
|
afText="Skapa ny Avvikelserapport (avvikelse)"
|
||||||
></digi-ng-link-button>
|
></digi-ng-link-button>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li *ngIf="periodiskRedovisningButtonVisible">
|
||||||
|
<digi-ng-link-button
|
||||||
|
class="deltagare-tab-reports__button"
|
||||||
|
afRoute="./periodisk-redovisning"
|
||||||
|
afText="Skapa ny Periodisk redovisning"
|
||||||
|
></digi-ng-link-button>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="signalButtonVisible">
|
||||||
<digi-ng-link-button
|
<digi-ng-link-button
|
||||||
class="deltagare-tab-reports__button"
|
class="deltagare-tab-reports__button"
|
||||||
afRoute="./signal"
|
afRoute="./signal"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
|
import { Feature } from '@msfa-enums/feature.enum';
|
||||||
|
import { environment } from '@msfa-environment';
|
||||||
import { ReportsData } from '@msfa-models/report.model';
|
import { ReportsData } from '@msfa-models/report.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -11,6 +13,15 @@ export class DeltagareTabReportsComponent {
|
|||||||
@Input() reportsData: ReportsData;
|
@Input() reportsData: ReportsData;
|
||||||
@Output() reportsPaginated = new EventEmitter<number>();
|
@Output() reportsPaginated = new EventEmitter<number>();
|
||||||
|
|
||||||
|
_activeFeatures: Feature[] = environment.activeFeatures;
|
||||||
|
|
||||||
|
get signalButtonVisible(): boolean {
|
||||||
|
return this._activeFeatures.includes(Feature.REPORTING_SIGNAL);
|
||||||
|
}
|
||||||
|
get periodiskRedovisningButtonVisible(): boolean {
|
||||||
|
return this._activeFeatures.includes(Feature.REPORTING_PERIODISK_REDOVISNING);
|
||||||
|
}
|
||||||
|
|
||||||
emitNewPage(page: number): void {
|
emitNewPage(page: number): void {
|
||||||
this.reportsPaginated.emit(page);
|
this.reportsPaginated.emit(page);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||||
import { Feature } from '@msfa-enums/feature.enum';
|
import { Feature } from '@msfa-enums/feature.enum';
|
||||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
@@ -19,7 +20,6 @@ import { HandledareService } from '@msfa-services/handledare.service';
|
|||||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||||
import { distinctUntilChanged, filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators';
|
import { distinctUntilChanged, filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators';
|
||||||
import { DeltagareCardService } from './deltagare-card.service';
|
import { DeltagareCardService } from './deltagare-card.service';
|
||||||
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-deltagare-details',
|
selector: 'msfa-deltagare-details',
|
||||||
@@ -113,7 +113,7 @@ export class DeltagareCardComponent extends UnsubscribeDirective {
|
|||||||
|
|
||||||
get sensitiveDataVisible(): boolean {
|
get sensitiveDataVisible(): boolean {
|
||||||
return (
|
return (
|
||||||
this._activeFeatures.includes(Feature.SENSITIVE_INFORMATION) &&
|
this._activeFeatures.includes(Feature.DELTAGARE_SENSITIVE_INFORMATION) &&
|
||||||
this._userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning)
|
this._userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,8 +129,7 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
</dl>
|
</dl>
|
||||||
<digi-notification-alert
|
<digi-notification-alert
|
||||||
*ngIf="error$ | async as error"
|
*ngIf="submitError$ | async as error"
|
||||||
class="avvikelse-report__alert"
|
|
||||||
af-variation="danger"
|
af-variation="danger"
|
||||||
af-heading="Någonting gick fel"
|
af-heading="Någonting gick fel"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
|
|||||||
reportingDateFormName: AvvikelseFormKeys = 'reportingDate';
|
reportingDateFormName: AvvikelseFormKeys = 'reportingDate';
|
||||||
|
|
||||||
submitIsLoading$ = new BehaviorSubject<boolean>(false);
|
submitIsLoading$ = new BehaviorSubject<boolean>(false);
|
||||||
error$ = new BehaviorSubject<CustomError>(null);
|
submitError$ = new BehaviorSubject<CustomError>(null);
|
||||||
|
|
||||||
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
||||||
map((params: Params) => +params.genomforandeReferens)
|
map((params: Params) => +params.genomforandeReferens)
|
||||||
@@ -110,10 +110,6 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
|
|||||||
this.subscriptions.push(
|
this.subscriptions.push(
|
||||||
this.chosenReason$.subscribe(() => {
|
this.chosenReason$.subscribe(() => {
|
||||||
this.shouldValidate$.next(false);
|
this.shouldValidate$.next(false);
|
||||||
}),
|
|
||||||
this.questionsForChosenReason$.subscribe(questions => {
|
|
||||||
this.clearQuestions();
|
|
||||||
questions.forEach(question => this.addQuestionToForm(question));
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -157,7 +153,7 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
|
|||||||
this.confirmDialogIsOpen$.next(false);
|
this.confirmDialogIsOpen$.next(false);
|
||||||
},
|
},
|
||||||
error: (customError: CustomError) => {
|
error: (customError: CustomError) => {
|
||||||
this.error$.next({ ...customError, message: customError.error.message });
|
this.submitError$.next({ ...customError, message: customError.error.message });
|
||||||
this.submitIsLoading$.next(false);
|
this.submitIsLoading$.next(false);
|
||||||
throw { ...customError, avoidToast: true };
|
throw { ...customError, avoidToast: true };
|
||||||
},
|
},
|
||||||
@@ -167,7 +163,7 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
cancelConfirmDialog(): void {
|
cancelConfirmDialog(): void {
|
||||||
this.confirmDialogIsOpen$.next(false);
|
this.confirmDialogIsOpen$.next(false);
|
||||||
this.error$.next(null);
|
this.submitError$.next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
|||||||
@@ -318,7 +318,7 @@
|
|||||||
<dd>{{expectedPresenceStartTimeFormControl.value}} - {{expectedPresenceEndTimeFormControl.value}}</dd>
|
<dd>{{expectedPresenceStartTimeFormControl.value}} - {{expectedPresenceEndTimeFormControl.value}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<digi-notification-alert
|
<digi-notification-alert
|
||||||
*ngIf="error$ | async as error"
|
*ngIf="submitError$ | async as error"
|
||||||
class="franvaro-report-form__alert"
|
class="franvaro-report-form__alert"
|
||||||
af-variation="danger"
|
af-variation="danger"
|
||||||
af-heading="Någonting gick fel"
|
af-heading="Någonting gick fel"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Avrop } from '@msfa-models/avrop.model';
|
|||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { FranvaroReason } from '@msfa-models/franvaro-reason.model';
|
import { FranvaroReason } from '@msfa-models/franvaro-reason.model';
|
||||||
import { Franvaro } from '@msfa-models/franvaro.model';
|
import { Franvaro } from '@msfa-models/franvaro.model';
|
||||||
import { dateToIsoString } from '@msfa-utils/format-to-date.util';
|
import { formatDate } from '@msfa-utils/format-to-date.util';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||||
import { FranvaroReportFormService } from './franvaro-report-form.service';
|
import { FranvaroReportFormService } from './franvaro-report-form.service';
|
||||||
@@ -39,7 +39,7 @@ export class FranvaroReportFormComponent {
|
|||||||
[FranvaroReportFormValidator.isFranvaroReportValid()]
|
[FranvaroReportFormValidator.isFranvaroReportValid()]
|
||||||
);
|
);
|
||||||
|
|
||||||
error$ = new BehaviorSubject<CustomError>(null);
|
submitError$ = new BehaviorSubject<CustomError>(null);
|
||||||
submitLoading$ = new BehaviorSubject<boolean>(false);
|
submitLoading$ = new BehaviorSubject<boolean>(false);
|
||||||
lastSubmittedFranvaroReport$ = new BehaviorSubject<Date>(null);
|
lastSubmittedFranvaroReport$ = new BehaviorSubject<Date>(null);
|
||||||
currentGenomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
currentGenomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
||||||
@@ -142,7 +142,7 @@ export class FranvaroReportFormComponent {
|
|||||||
|
|
||||||
cancelConfirmDialog(): void {
|
cancelConfirmDialog(): void {
|
||||||
this.confirmDialogOpen$.next(false);
|
this.confirmDialogOpen$.next(false);
|
||||||
this.error$.next(null);
|
this.submitError$.next(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
reasonChanged(): void {
|
reasonChanged(): void {
|
||||||
@@ -168,7 +168,7 @@ export class FranvaroReportFormComponent {
|
|||||||
genomforandeReferens: +genomforandeReferens,
|
genomforandeReferens: +genomforandeReferens,
|
||||||
franvaro: {
|
franvaro: {
|
||||||
avvikelseOrsaksKod: reason,
|
avvikelseOrsaksKod: reason,
|
||||||
datum: dateToIsoString(date),
|
datum: formatDate(date),
|
||||||
heldag: wholeDay,
|
heldag: wholeDay,
|
||||||
startTid: this.showTimePickers ? startTime : '0:00', // BÄR doesn't accept empty string or null
|
startTid: this.showTimePickers ? startTime : '0:00', // BÄR doesn't accept empty string or null
|
||||||
slutTid: this.showTimePickers ? endTime : '23:59', // BÄR doesn't accept empty string or null
|
slutTid: this.showTimePickers ? endTime : '23:59', // BÄR doesn't accept empty string or null
|
||||||
@@ -195,7 +195,7 @@ export class FranvaroReportFormComponent {
|
|||||||
this.confirmDialogOpen$.next(false);
|
this.confirmDialogOpen$.next(false);
|
||||||
},
|
},
|
||||||
error: (customError: CustomError) => {
|
error: (customError: CustomError) => {
|
||||||
this.error$.next({ ...customError, message: customError.error.message });
|
this.submitError$.next({ ...customError, message: customError.error.message });
|
||||||
this.submitLoading$.next(false);
|
this.submitLoading$.next(false);
|
||||||
throw { ...customError, avoidToast: true };
|
throw { ...customError, avoidToast: true };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { formatDate } from '@msfa-utils/format-to-date.util';
|
||||||
|
import { parseISO } from 'date-fns';
|
||||||
|
import { extractAvropPeriods } from './extract-avrop-periods';
|
||||||
|
|
||||||
|
describe('extract-avrop-periods', function () {
|
||||||
|
describe('when avrop is between 2021-05-01 and 2021-08-15', function () {
|
||||||
|
const startDate = parseISO('2021-05-01');
|
||||||
|
const endDate = parseISO('2021-08-15');
|
||||||
|
const periods = extractAvropPeriods(startDate, endDate);
|
||||||
|
|
||||||
|
it('should yield 3 periods between 2021-05-01 and 2021-08-15', function () {
|
||||||
|
expect(periods.map(period => period.periodId)).toEqual(['2021-05', '2021-06', '2021-07', '2021-08']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('first period should start at startDate', function () {
|
||||||
|
expect(periods[0].startDate).toEqual(startDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('second period should start with first day of month', function () {
|
||||||
|
expect(formatDate(periods[1].startDate)).toEqual('2021-06-01');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('second period should end with last day of month', function () {
|
||||||
|
expect(formatDate(periods[1].endDate)).toEqual('2021-06-30');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('last period should end with last day of avrop', function () {
|
||||||
|
expect(formatDate(periods[periods.length - 1].endDate)).toEqual('2021-08-15');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if startDate is greater than endDate', function () {
|
||||||
|
const startDate = parseISO('2021-09-01');
|
||||||
|
const endDate = parseISO('2021-08-15');
|
||||||
|
expect(() => extractAvropPeriods(startDate, endDate)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if startDate is null', function () {
|
||||||
|
const startDate = null;
|
||||||
|
const endDate = parseISO('2021-08-15');
|
||||||
|
expect(() => extractAvropPeriods(startDate, endDate)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if endDate is null', function () {
|
||||||
|
const startDate = parseISO('2021-08-15');
|
||||||
|
const endDate = null;
|
||||||
|
expect(() => extractAvropPeriods(startDate, endDate)).toThrowError();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work if startDate is end of short month', function () {
|
||||||
|
const startDate = parseISO('2021-02-28');
|
||||||
|
const endDate = parseISO('2021-03-31');
|
||||||
|
const periods = extractAvropPeriods(startDate, endDate);
|
||||||
|
expect(periods.map(period => period.periodId)).toEqual(['2021-02', '2021-03']);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { AvropPeriod } from '@msfa-models/avrop-period.model';
|
||||||
|
import { addMonths, endOfMonth, formatISO, startOfMonth, subMonths } from 'date-fns';
|
||||||
|
|
||||||
|
export function dateToPeriodId(date: Date): string {
|
||||||
|
return formatISO(date).slice(0, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractAvropPeriods(avropStartDate: Date, avropEndDate: Date): AvropPeriod[] {
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
|
if (avropStartDate > today) {
|
||||||
|
throw new Error('Avropet har inte börjat ännu.');
|
||||||
|
}
|
||||||
|
if (avropStartDate > avropEndDate) {
|
||||||
|
throw new Error('Avropets startdatum måste komma innan slutdatumet.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousPeriod = dateToPeriodId(subMonths(today, 1));
|
||||||
|
const periods: AvropPeriod[] = [] as AvropPeriod[];
|
||||||
|
let dateCounter = avropStartDate;
|
||||||
|
|
||||||
|
while (dateCounter <= avropEndDate && dateToPeriodId(dateCounter) <= previousPeriod) {
|
||||||
|
const startDate = dateCounter === avropStartDate ? avropStartDate : startOfMonth(dateCounter);
|
||||||
|
const endDate = endOfMonth(dateCounter) > avropEndDate ? avropEndDate : endOfMonth(dateCounter);
|
||||||
|
const period: AvropPeriod = { startDate, endDate, periodId: dateToPeriodId(dateCounter) };
|
||||||
|
periods.push(period);
|
||||||
|
dateCounter = addMonths(dateCounter, 1);
|
||||||
|
}
|
||||||
|
return periods;
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<msfa-layout>
|
||||||
|
<msfa-report-layout
|
||||||
|
*ngIf="avrop$ | async as avrop; else skeletonRef"
|
||||||
|
reportTitle="Skapa Periodisk redovisning"
|
||||||
|
[avrop]="avrop"
|
||||||
|
>
|
||||||
|
<div class="periodisk-redovisning-form" *ngIf="genomforandeReferens$ | async as genomforandeReferens">
|
||||||
|
<div
|
||||||
|
class="periodisk-redovisning-form__confirmation"
|
||||||
|
*ngIf="submittedDate$ | async as submittedDate; else formRef"
|
||||||
|
>
|
||||||
|
<digi-notification-alert
|
||||||
|
class="periodisk-redovisning-form__alert"
|
||||||
|
af-variation="success"
|
||||||
|
af-heading="Allt gick bra"
|
||||||
|
af-heading-level="h3"
|
||||||
|
>
|
||||||
|
<p>Periodisk redovisning för deltagare {{avrop.fullName}} är nu inskickad till Arbetsförmedlingen.</p>
|
||||||
|
<dl>
|
||||||
|
<dt>Datum</dt>
|
||||||
|
<dd>{{submittedDate | date:'longDate'}} kl {{submittedDate | date:'shortTime'}}</dd>
|
||||||
|
</dl>
|
||||||
|
</digi-notification-alert>
|
||||||
|
<msfa-back-link route="../">Tillbaka till deltagaren</msfa-back-link>
|
||||||
|
</div>
|
||||||
|
<ng-template #formRef>
|
||||||
|
<form
|
||||||
|
*ngIf="periods$ | async as periods; else loadingRef"
|
||||||
|
class="periodisk-redovisning-form__form"
|
||||||
|
[formGroup]="formGroup"
|
||||||
|
(ngSubmit)="openConfirmDialog()"
|
||||||
|
id="periodisk-redovisning-form"
|
||||||
|
>
|
||||||
|
<digi-ng-form-select
|
||||||
|
afLabel="Period"
|
||||||
|
[afSelectItems]="periodsToFormselectItems(periods)"
|
||||||
|
[formControl]="periodFormControl"
|
||||||
|
[afInvalid]="formControlIsInvalid(periodFormControl)"
|
||||||
|
></digi-ng-form-select>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<digi-form-fieldset
|
||||||
|
af-legend="Har ni, under perioden, tillhandahållit språkstöd?"
|
||||||
|
af-name="languageSupport"
|
||||||
|
af-form="periodisk-redovisning-form"
|
||||||
|
>
|
||||||
|
<digi-ng-form-radiobutton-group
|
||||||
|
[afRadiobuttonGroupDirection]="radiobuttonGroupDirection.HORIZONTAL"
|
||||||
|
[afRadiobuttons]="[{label:'Ja', value: true}, {label:'Nej', value: false}]"
|
||||||
|
[formControl]="hasOfferedLanguageSupportFormControl"
|
||||||
|
></digi-ng-form-radiobutton-group>
|
||||||
|
</digi-form-fieldset>
|
||||||
|
<digi-form-validation-message
|
||||||
|
*ngIf="formControlIsInvalid(hasOfferedLanguageSupportFormControl)"
|
||||||
|
af-variation="error"
|
||||||
|
>
|
||||||
|
Ett val är obligatoriskt
|
||||||
|
</digi-form-validation-message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<digi-form-fieldset
|
||||||
|
af-legend="Har ni erbjudit arbete?"
|
||||||
|
af-name="jobOffered"
|
||||||
|
af-form="periodisk-redovisning-form"
|
||||||
|
>
|
||||||
|
<digi-ng-form-radiobutton-group
|
||||||
|
[afRadiobuttonGroupDirection]="radiobuttonGroupDirection.HORIZONTAL"
|
||||||
|
[afRadiobuttons]="[{label:'Ja', value: true}, {label:'Nej', value: false}]"
|
||||||
|
[formControl]="hasOfferedJobFormControl"
|
||||||
|
></digi-ng-form-radiobutton-group>
|
||||||
|
</digi-form-fieldset>
|
||||||
|
<digi-form-validation-message *ngIf="formControlIsInvalid(hasOfferedJobFormControl)" af-variation="error">
|
||||||
|
Ett val är obligatoriskt
|
||||||
|
</digi-form-validation-message>
|
||||||
|
</div>
|
||||||
|
<digi-form-fieldset
|
||||||
|
af-legend="Ange aktiviteter som har utförts under perioden"
|
||||||
|
af-name="activities"
|
||||||
|
af-form="periodisk-redovisning-form"
|
||||||
|
>
|
||||||
|
<div class="periodisk-redovisning-form__no-activities-has-been-conducted-checkbox">
|
||||||
|
<digi-ng-form-checkbox
|
||||||
|
formControlName="noActivitiesHasBeenConducted"
|
||||||
|
afLabel="Deltagaren har inte deltagit i några aktiviteter denna period"
|
||||||
|
[afInvalid]="this.shouldValidate$.value && !!formErrors?.activitiesMismatch"
|
||||||
|
></digi-ng-form-checkbox>
|
||||||
|
<ng-container *ngIf="this.shouldValidate$.value && !!formErrors?.activitiesMismatch">
|
||||||
|
<digi-form-validation-message af-variation="error">
|
||||||
|
{{formErrors?.activitiesMismatch}}
|
||||||
|
</digi-form-validation-message>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
[formArrayName]="ACTIVITES_FORM_NAME"
|
||||||
|
class="periodisk-redovisning-form__activity-checkboxes"
|
||||||
|
*ngFor="let activityFormGroup of activitiesFormArray.controls; let i=index"
|
||||||
|
>
|
||||||
|
<div [formGroupName]="i" class="">
|
||||||
|
<digi-ng-form-checkbox
|
||||||
|
#isSelected
|
||||||
|
formControlName="isSelected"
|
||||||
|
[afLabel]="activitiesFormArrayMetadata[i].name"
|
||||||
|
></digi-ng-form-checkbox>
|
||||||
|
<div class="periodisk-redovisning-form__activity-location-checkboxes">
|
||||||
|
<digi-ng-form-checkbox
|
||||||
|
*ngIf="isSelected.currentValue"
|
||||||
|
formControlName="performedRemotely"
|
||||||
|
[afInvalid]="activityLocationIsInvalid(activityFormGroup)"
|
||||||
|
afLabel="Utfört på distans"
|
||||||
|
></digi-ng-form-checkbox>
|
||||||
|
<digi-ng-form-checkbox
|
||||||
|
*ngIf="isSelected.currentValue"
|
||||||
|
formControlName="performedPhysically"
|
||||||
|
[afInvalid]="activityLocationIsInvalid(activityFormGroup)"
|
||||||
|
afLabel="Utfört på plats"
|
||||||
|
></digi-ng-form-checkbox>
|
||||||
|
<ng-container *ngIf="formControlIsInvalid(activityFormGroup)">
|
||||||
|
<digi-form-validation-message
|
||||||
|
*ngFor="let errorText of errorsToArray(activityFormGroup.errors)"
|
||||||
|
af-variation="error"
|
||||||
|
>
|
||||||
|
{{errorText}}
|
||||||
|
</digi-form-validation-message>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</digi-form-fieldset>
|
||||||
|
|
||||||
|
<footer class="periodisk-redovisning-form__footer">
|
||||||
|
<div class="periodisk-redovisning-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"> och gå tillbaka till deltagaren</span>
|
||||||
|
</msfa-back-link>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<digi-ng-dialog
|
||||||
|
[afActive]="confirmDialogIsOpen$ | async"
|
||||||
|
(afOnPrimaryClick)="submitAndCloseConfirmDialog(genomforandeReferens)"
|
||||||
|
(afOnInactive)="cancelConfirmDialog()"
|
||||||
|
afHeadingLevel="h2"
|
||||||
|
afPrimaryButtonText="Skicka in"
|
||||||
|
afSecondaryButtonText="Avbryt"
|
||||||
|
(afOnSecondaryClick)="cancelConfirmDialog()"
|
||||||
|
afHeading="Vill du skicka in Periodisk redovisning"
|
||||||
|
afAriaLabel="Förhandsgranska och skicka in Periodisk redovisning"
|
||||||
|
id="confirm-periodisk-redovisning-form"
|
||||||
|
>
|
||||||
|
<msfa-loader *ngIf="submitIsLoading$ | async" type="absolute"></msfa-loader>
|
||||||
|
<dl>
|
||||||
|
<dt>Namn</dt>
|
||||||
|
<dd>{{avrop.fullName}}</dd>
|
||||||
|
<dt>Personnummer</dt>
|
||||||
|
<dd>
|
||||||
|
<msfa-hide-text
|
||||||
|
symbols="********-****"
|
||||||
|
[changingText]="avrop.ssn"
|
||||||
|
ariaLabelType="Personnummer"
|
||||||
|
></msfa-hide-text>
|
||||||
|
</dd>
|
||||||
|
<dt>Tjänst</dt>
|
||||||
|
<dd>{{avrop.tjanst}}</dd>
|
||||||
|
<dt>Startdatum</dt>
|
||||||
|
<dd>
|
||||||
|
<digi-typography-time [afDateTime]="avrop.startDate"></digi-typography-time>
|
||||||
|
</dd>
|
||||||
|
<dt>Slutdatum</dt>
|
||||||
|
<dd>
|
||||||
|
<digi-typography-time [afDateTime]="avrop.endDate"></digi-typography-time>
|
||||||
|
</dd>
|
||||||
|
<ng-container *ngIf="submitData$ | async; let submitData; else loadingRef">
|
||||||
|
<dt>Har ni, under perioden, tillhandahållit språkstöd:</dt>
|
||||||
|
<dd>{{submitData.hasOfferedLanguageSupport ? 'Ja' : 'Nej' }}</dd>
|
||||||
|
|
||||||
|
<dt>Har ni, under perioden, erbjudit arbete:</dt>
|
||||||
|
<dd>{{submitData.hasOfferedJob ? 'Ja' : 'Nej' }}</dd>
|
||||||
|
|
||||||
|
<dt>Aktiviteter som utförts under perioden:</dt>
|
||||||
|
<ng-container *ngIf="submitData.activities.length === 0"
|
||||||
|
>Deltagaren har inte deltagit i några aktiviteter denna period
|
||||||
|
</ng-container>
|
||||||
|
<ng-container *ngFor="let activity of submitData.activities">
|
||||||
|
<dd>
|
||||||
|
{{getActivityMetadata(activity.id).name}}: {{ activity.performedRemotely &&
|
||||||
|
activity.performedPhysically ? 'På distans och på plats' : activity.performedRemotely ? 'På distans' :
|
||||||
|
'På plats'}}
|
||||||
|
</dd>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</dl>
|
||||||
|
<digi-notification-alert
|
||||||
|
*ngIf="submitError$ | async as error"
|
||||||
|
af-variation="danger"
|
||||||
|
af-heading="Någonting gick fel"
|
||||||
|
>
|
||||||
|
<p>Kunde inte spara Periodisk redovisning. 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>
|
||||||
|
</div>
|
||||||
|
</msfa-report-layout>
|
||||||
|
</msfa-layout>
|
||||||
|
|
||||||
|
<ng-template #skeletonRef>
|
||||||
|
<digi-ng-skeleton-base
|
||||||
|
[afCount]="3"
|
||||||
|
afText="Laddar data för att kunna skapa Periodisk redovisning"
|
||||||
|
></digi-ng-skeleton-base>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<ng-template #loadingRef>
|
||||||
|
<msfa-loader type="padded"></msfa-loader>
|
||||||
|
</ng-template>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
@import 'mixins/list';
|
||||||
|
@import 'variables/gutters';
|
||||||
|
|
||||||
|
.periodisk-redovisning-form {
|
||||||
|
max-width: var(--digi--typography--text--max-width);
|
||||||
|
|
||||||
|
&__confirmation,
|
||||||
|
&__warning,
|
||||||
|
&__form {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $digi--layout--gutter--l;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--digi--layout--gutter);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cta-wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--digi--layout--gutter);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__no-activities-has-been-conducted-checkbox {
|
||||||
|
margin-bottom: $digi--layout--gutter--l;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__activity-checkboxes {
|
||||||
|
margin-bottom: var(--digi--layout--gutter--s);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__activity-location-checkboxes {
|
||||||
|
margin-left: var(--digi--layout--gutter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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 { PeriodiskRedovisningFormComponent } from './periodisk-redovisning-form.component';
|
||||||
|
import { PeriodiskRedovisningFormService } from './periodisk-redovisning-form.service';
|
||||||
|
|
||||||
|
describe('PeriodiskRedovisningFormComponent', () => {
|
||||||
|
let component: PeriodiskRedovisningFormComponent;
|
||||||
|
let fixture: ComponentFixture<PeriodiskRedovisningFormComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [PeriodiskRedovisningFormComponent, LayoutComponent],
|
||||||
|
imports: [
|
||||||
|
RouterTestingModule,
|
||||||
|
HttpClientTestingModule,
|
||||||
|
DigiNgFormRadiobuttonGroupModule,
|
||||||
|
DigiNgFormCheckboxModule,
|
||||||
|
],
|
||||||
|
providers: [PeriodiskRedovisningFormService],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(PeriodiskRedovisningFormComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
import { FormSelectItem } from '@af/digi-ng/_form/form-select';
|
||||||
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
|
import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';
|
||||||
|
import { ActivatedRoute, Params } from '@angular/router';
|
||||||
|
import { Avrop } from '@msfa-models/avrop.model';
|
||||||
|
import { AvropPeriod } from '@msfa-models/avrop-period.model';
|
||||||
|
import { formatDate } from '@msfa-utils/format-to-date.util';
|
||||||
|
import { subMonths } from 'date-fns';
|
||||||
|
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||||
|
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||||
|
import { dateToPeriodId, extractAvropPeriods } from './extract-avrop-periods';
|
||||||
|
import { PeriodiskRedovisningFormService } from './periodisk-redovisning-form.service';
|
||||||
|
import { RadiobuttonGroupDirection } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||||
|
import { RequiredValidator } from '@msfa-validators/required.validator';
|
||||||
|
import { Activity } from '@msfa-models/activity.model';
|
||||||
|
import { GemensamPlaneringApiService } from '@msfa-services/api/gemensam-planering-api.service';
|
||||||
|
import {
|
||||||
|
PeriodiskRedovisningActivityRequest,
|
||||||
|
PeriodiskRedovisningRequest,
|
||||||
|
} from '@msfa-models/api/periodisk-redovisning.request.model';
|
||||||
|
import { DateFormatOptions } from '@msfa-models/date-format-options.model';
|
||||||
|
import {
|
||||||
|
ActivityFormErrors,
|
||||||
|
PeriodiskRedovisningFormData,
|
||||||
|
PeriodiskRedovisningFormErrors,
|
||||||
|
PeriodiskRedovisningFormKeys,
|
||||||
|
} from './periodisk-redovisning-form.model';
|
||||||
|
import { PeriodiskRedovisningValidator } from './periodisk-redovisning.validator';
|
||||||
|
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'msfa-periodisk-redovisning-form',
|
||||||
|
templateUrl: './periodisk-redovisning-form.component.html',
|
||||||
|
styleUrls: ['./periodisk-redovisning-form.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class PeriodiskRedovisningFormComponent extends UnsubscribeDirective implements OnInit {
|
||||||
|
readonly PERIOD_FORM_NAME: PeriodiskRedovisningFormKeys = 'period';
|
||||||
|
readonly HAS_OFFERED_LANGUAGE_SUPPORT_FORM_NAME: PeriodiskRedovisningFormKeys = 'hasOfferedLanguageSupport';
|
||||||
|
readonly HAS_OFFERED_JOB_FORM_NAME: PeriodiskRedovisningFormKeys = 'hasOfferedJob';
|
||||||
|
readonly ACTIVITES_FORM_NAME: PeriodiskRedovisningFormKeys = 'activities';
|
||||||
|
readonly NO_ACTIVITIES_HAS_BEEN_CONDUCTED_FORM_NAME: PeriodiskRedovisningFormKeys = 'noActivitiesHasBeenConducted';
|
||||||
|
|
||||||
|
today = new Date();
|
||||||
|
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||||
|
confirmDialogIsOpen$ = new BehaviorSubject<boolean>(false);
|
||||||
|
submittedDate$ = new BehaviorSubject<Date>(null);
|
||||||
|
submitIsLoading$ = new BehaviorSubject<boolean>(false);
|
||||||
|
radiobuttonGroupDirection = RadiobuttonGroupDirection;
|
||||||
|
activitiesFormArrayMetadata: Activity[];
|
||||||
|
submitError$ = new BehaviorSubject<CustomError>(null);
|
||||||
|
|
||||||
|
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
|
||||||
|
map((params: Params) => +params.genomforandeReferens)
|
||||||
|
);
|
||||||
|
avrop$: Observable<Avrop> = this.genomforandeReferens$.pipe(
|
||||||
|
switchMap(genomforandeReferens =>
|
||||||
|
this.periodiskRedovisningFormService.fetchAvropInformation$(genomforandeReferens)
|
||||||
|
),
|
||||||
|
shareReplay(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
availableActivities$: Observable<Activity[]> = this.gemensamPlaneringApiService.fetchActivities$();
|
||||||
|
|
||||||
|
periods$: Observable<AvropPeriod[]> = this.avrop$.pipe(
|
||||||
|
map(avrop => extractAvropPeriods(avrop.startDate, avrop.endDate))
|
||||||
|
);
|
||||||
|
previousPeriod = dateToPeriodId(subMonths(new Date(), 1));
|
||||||
|
formGroup = new FormGroup(
|
||||||
|
{
|
||||||
|
[this.PERIOD_FORM_NAME]: new FormControl(this.previousPeriod, RequiredValidator()),
|
||||||
|
[this.HAS_OFFERED_LANGUAGE_SUPPORT_FORM_NAME]: new FormControl(null, RequiredValidator()),
|
||||||
|
[this.HAS_OFFERED_JOB_FORM_NAME]: new FormControl(null, RequiredValidator()),
|
||||||
|
[this.ACTIVITES_FORM_NAME]: new FormArray([]),
|
||||||
|
[this.NO_ACTIVITIES_HAS_BEEN_CONDUCTED_FORM_NAME]: new FormControl(),
|
||||||
|
},
|
||||||
|
[PeriodiskRedovisningValidator.periodiskRedovisningIsValid()]
|
||||||
|
);
|
||||||
|
|
||||||
|
formData$: Observable<PeriodiskRedovisningFormData> = this.formGroup
|
||||||
|
.valueChanges as Observable<PeriodiskRedovisningFormData>;
|
||||||
|
|
||||||
|
submitData$ = combineLatest([this.genomforandeReferens$, this.formData$]).pipe(
|
||||||
|
map(([genomforandeReferens, formData]) => this.formDataToSubmitData(genomforandeReferens, formData)),
|
||||||
|
shareReplay(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
periodsToFormselectItems(periods: AvropPeriod[]): FormSelectItem[] {
|
||||||
|
return periods.map(period => ({
|
||||||
|
name: `${formatDate(period.startDate, 'sv-SE', { month: 'long', year: 'numeric' } as DateFormatOptions)}`,
|
||||||
|
value: period.periodId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
get formErrors(): PeriodiskRedovisningFormErrors {
|
||||||
|
return this.formGroup.errors as PeriodiskRedovisningFormErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private periodiskRedovisningFormService: PeriodiskRedovisningFormService,
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
// TODO. GemensamPlaneringApiService is used for fetching activities. Replace with own service once ready
|
||||||
|
private gemensamPlaneringApiService: GemensamPlaneringApiService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
super.unsubscribeOnDestroy(
|
||||||
|
this.formGroup.valueChanges.subscribe(value => {
|
||||||
|
this.shouldValidate$.next(false);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get periodFormControl(): AbstractControl | undefined {
|
||||||
|
return this.formGroup.get(this.PERIOD_FORM_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasOfferedLanguageSupportFormControl(): AbstractControl | undefined {
|
||||||
|
return this.formGroup.get(this.HAS_OFFERED_LANGUAGE_SUPPORT_FORM_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasOfferedJobFormControl(): AbstractControl | undefined {
|
||||||
|
return this.formGroup.get(this.HAS_OFFERED_JOB_FORM_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
get noActivitiesHasBeenConductedFormControl(): AbstractControl | undefined {
|
||||||
|
return this.formGroup.get(this.NO_ACTIVITIES_HAS_BEEN_CONDUCTED_FORM_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
get activitiesFormArray(): FormArray {
|
||||||
|
return this.formGroup.get(this.ACTIVITES_FORM_NAME) as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
openConfirmDialog(): void {
|
||||||
|
this.shouldValidate$.next(true);
|
||||||
|
|
||||||
|
if (this.formGroup.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.confirmDialogIsOpen$.next(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelConfirmDialog(): void {
|
||||||
|
this.confirmDialogIsOpen$.next(false);
|
||||||
|
this.submitError$.next(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
submitAndCloseConfirmDialog(genomforandeReferens: number): void {
|
||||||
|
this.submitIsLoading$.next(true);
|
||||||
|
this.submitData$.pipe(take(1)).subscribe(submitData =>
|
||||||
|
this.periodiskRedovisningFormService.submitPeriodiskRedovisning$(submitData).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.submitIsLoading$.next(false);
|
||||||
|
this.submittedDate$.next(new Date());
|
||||||
|
this.confirmDialogIsOpen$.next(false);
|
||||||
|
},
|
||||||
|
error: (customError: CustomError) => {
|
||||||
|
this.submitError$.next({ ...customError, message: customError.error.message });
|
||||||
|
this.submitIsLoading$.next(false);
|
||||||
|
throw { ...customError, avoidToast: true };
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formControlIsInvalid(formControl: AbstractControl): boolean {
|
||||||
|
return formControl.invalid && (formControl.touched || this.shouldValidate$.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearActivities(): void {
|
||||||
|
this.activitiesFormArray.clear();
|
||||||
|
this.activitiesFormArrayMetadata = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private addActivityToForm(activity: Activity): void {
|
||||||
|
// FormArray doesnt hold any IDs so we need to store these seperately and rebuild structure at submit
|
||||||
|
// It's important that the metadata is updated at the same time as the formArray to avoid sync problems
|
||||||
|
this.activitiesFormArrayMetadata.push(activity);
|
||||||
|
|
||||||
|
this.activitiesFormArray.push(
|
||||||
|
new FormGroup(
|
||||||
|
{
|
||||||
|
isSelected: new FormControl(),
|
||||||
|
performedRemotely: new FormControl(),
|
||||||
|
performedPhysically: new FormControl(),
|
||||||
|
},
|
||||||
|
PeriodiskRedovisningValidator.activityIsValid()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.availableActivities$.subscribe(activities => {
|
||||||
|
this.clearActivities();
|
||||||
|
activities.forEach(activity => this.addActivityToForm(activity));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private formDataToSubmitData(
|
||||||
|
genomforandeReferens: number,
|
||||||
|
formData: PeriodiskRedovisningFormData
|
||||||
|
): PeriodiskRedovisningRequest {
|
||||||
|
const { period, hasOfferedJob, hasOfferedLanguageSupport } = formData;
|
||||||
|
const activities: PeriodiskRedovisningActivityRequest[] = formData.activities
|
||||||
|
.filter(activity => activity.isSelected)
|
||||||
|
.map(({ performedPhysically, performedRemotely }, index) => ({
|
||||||
|
id: this.activitiesFormArrayMetadata[index].id,
|
||||||
|
performedPhysically: performedPhysically ?? false,
|
||||||
|
performedRemotely: performedRemotely ?? false,
|
||||||
|
}));
|
||||||
|
return {
|
||||||
|
genomforandeReferens,
|
||||||
|
period,
|
||||||
|
hasOfferedJob,
|
||||||
|
hasOfferedLanguageSupport,
|
||||||
|
activities,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
errorsToArray(errors: { [key: string]: string }): string[] {
|
||||||
|
return Object.values(errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
activityLocationIsInvalid(activityFormGroup: AbstractControl): boolean {
|
||||||
|
const errors = activityFormGroup.errors as ActivityFormErrors;
|
||||||
|
return this.formControlIsInvalid(activityFormGroup) && !!errors.locationCheckboxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
getActivityMetadata(activityId: number) {
|
||||||
|
return this.activitiesFormArrayMetadata.find(activity => activity.id === activityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export interface PeriodiskRedovisningFormActivity {
|
||||||
|
isSelected: boolean;
|
||||||
|
performedRemotely: boolean;
|
||||||
|
performedPhysically: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodiskRedovisningFormData {
|
||||||
|
period: string;
|
||||||
|
hasOfferedLanguageSupport: boolean;
|
||||||
|
hasOfferedJob: boolean;
|
||||||
|
noActivitiesHasBeenConducted: boolean;
|
||||||
|
activities: PeriodiskRedovisningFormActivity[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PeriodiskRedovisningFormKeys = keyof PeriodiskRedovisningFormData;
|
||||||
|
|
||||||
|
export interface PeriodiskRedovisningFormErrors {
|
||||||
|
activitiesMismatch?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActivityFormErrors {
|
||||||
|
locationCheckboxes?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
||||||
|
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||||
|
import { 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 { PeriodiskRedovisningFormComponent } from './periodisk-redovisning-form.component';
|
||||||
|
import { PeriodiskRedovisningFormService } from './periodisk-redovisning-form.service';
|
||||||
|
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
|
||||||
|
import { DigiNgFormRadiobuttonGroupModule } from '@af/digi-ng/_form/form-radiobutton-group';
|
||||||
|
import { DigiNgFormCheckboxModule } from '@af/digi-ng/_form/form-checkbox';
|
||||||
|
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [PeriodiskRedovisningFormComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule.forChild([{ path: '', component: PeriodiskRedovisningFormComponent }]),
|
||||||
|
LayoutModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
ReportLayoutModule,
|
||||||
|
BackLinkModule,
|
||||||
|
LoaderModule,
|
||||||
|
DigiNgSkeletonBaseModule,
|
||||||
|
DigiNgDialogModule,
|
||||||
|
DigiNgFormSelectModule,
|
||||||
|
DigiNgFormRadiobuttonGroupModule,
|
||||||
|
DigiNgFormCheckboxModule,
|
||||||
|
HideTextModule,
|
||||||
|
],
|
||||||
|
providers: [PeriodiskRedovisningFormService],
|
||||||
|
exports: [PeriodiskRedovisningFormComponent],
|
||||||
|
})
|
||||||
|
export class PeriodiskRedovisningFormModule {}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Avrop } from '@msfa-models/avrop.model';
|
||||||
|
import { PeriodiskRedovisning } from '@msfa-models/periodisk-redovisning.model';
|
||||||
|
import { DeltagareApiService } from '@msfa-services/api/deltagare.api.service';
|
||||||
|
import { PeriodiskRedovisningApiService } from '@msfa-services/api/periodisk-redovisning.api.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { PeriodiskRedovisningRequest } from '@msfa-models/api/periodisk-redovisning.request.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PeriodiskRedovisningFormService {
|
||||||
|
constructor(
|
||||||
|
private periodiskRedovisningApiService: PeriodiskRedovisningApiService,
|
||||||
|
private deltagareApiService: DeltagareApiService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fetchPeriodiskRedovisning$(
|
||||||
|
periodStart: string,
|
||||||
|
periodEnd: string,
|
||||||
|
genomforandeReferens: number
|
||||||
|
): Observable<PeriodiskRedovisning> {
|
||||||
|
return this.periodiskRedovisningApiService.fetchPeriodiskRedovisning$(periodStart, periodEnd, genomforandeReferens);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAvropInformation$(genomforandeReferens: number): Observable<Avrop> {
|
||||||
|
return this.deltagareApiService.fetchAvropInformation$(genomforandeReferens);
|
||||||
|
}
|
||||||
|
|
||||||
|
submitPeriodiskRedovisning$(submitData: PeriodiskRedovisningRequest): Observable<void> {
|
||||||
|
return this.periodiskRedovisningApiService.postPeriodiskRedovisning$(submitData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||||
|
import {
|
||||||
|
ActivityFormErrors,
|
||||||
|
PeriodiskRedovisningFormActivity,
|
||||||
|
PeriodiskRedovisningFormData,
|
||||||
|
PeriodiskRedovisningFormErrors,
|
||||||
|
} from './periodisk-redovisning-form.model';
|
||||||
|
|
||||||
|
export class PeriodiskRedovisningValidator {
|
||||||
|
static periodiskRedovisningIsValid(): ValidatorFn {
|
||||||
|
return (c: AbstractControl): PeriodiskRedovisningFormErrors => {
|
||||||
|
let errors: PeriodiskRedovisningFormErrors;
|
||||||
|
|
||||||
|
const { noActivitiesHasBeenConducted, activities } = c.value as PeriodiskRedovisningFormData;
|
||||||
|
if (noActivitiesHasBeenConducted && activities.filter(activity => activity.isSelected).length > 0) {
|
||||||
|
errors = {
|
||||||
|
...errors,
|
||||||
|
activitiesMismatch:
|
||||||
|
'Ifall kryssrutan "Deltagaren har inte deltagit i några aktiviteter denna period" är förbockad får inga aktiviteter vara valda. ',
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
noActivitiesHasBeenConducted !== true &&
|
||||||
|
activities.filter(activity => activity.isSelected).length === 0
|
||||||
|
) {
|
||||||
|
errors = {
|
||||||
|
...errors,
|
||||||
|
activitiesMismatch:
|
||||||
|
'Ifall inga aktiviteter är valda ska "Deltagaren har inte deltagit i några aktiviteter denna period" vara förbockad. ',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static activityIsValid(): ValidatorFn {
|
||||||
|
return (c: AbstractControl): ActivityFormErrors => {
|
||||||
|
let errors: ActivityFormErrors;
|
||||||
|
|
||||||
|
const { performedRemotely, performedPhysically, isSelected } = c.value as PeriodiskRedovisningFormActivity;
|
||||||
|
if (isSelected && !performedPhysically && !performedRemotely) {
|
||||||
|
errors = {
|
||||||
|
...errors,
|
||||||
|
locationCheckboxes: 'Minst en plats måste väljas',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import { SignalRequest } from '@msfa-models/api/signal.request.model';
|
|||||||
import { Avrop } from '@msfa-models/avrop.model';
|
import { Avrop } from '@msfa-models/avrop.model';
|
||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { Signal } from '@msfa-models/signal.model';
|
import { Signal } from '@msfa-models/signal.model';
|
||||||
import { dateToIsoString } from '@msfa-utils/format-to-date.util';
|
import { formatDate } from '@msfa-utils/format-to-date.util';
|
||||||
import { add } from 'date-fns';
|
import { add } from 'date-fns';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
|
||||||
@@ -125,7 +125,7 @@ export class SignalFormComponent {
|
|||||||
typ: type,
|
typ: type,
|
||||||
omfattning,
|
omfattning,
|
||||||
omfattning_procent: omfattning === 'deltid' ? percent : null,
|
omfattning_procent: omfattning === 'deltid' ? percent : null,
|
||||||
startdatum: dateToIsoString(startDate),
|
startdatum: formatDate(startDate),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.signalFormService
|
this.signalFormService
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
export enum Feature {
|
export enum Feature {
|
||||||
AVROP,
|
LOGGING,
|
||||||
DELTAGARE,
|
|
||||||
ADMINISTRATION,
|
|
||||||
MY_ACCOUNT,
|
|
||||||
MY_ORGANIZATION,
|
|
||||||
RELEASES,
|
RELEASES,
|
||||||
MOCK_LOGIN,
|
MOCK_LOGIN,
|
||||||
VERSION_INFO,
|
VERSION_INFO,
|
||||||
|
|
||||||
|
AVROP,
|
||||||
|
DELTAGARE,
|
||||||
|
DELTAGARE_SENSITIVE_INFORMATION,
|
||||||
|
ADMINISTRATION,
|
||||||
|
MY_ACCOUNT,
|
||||||
|
MY_ORGANIZATION,
|
||||||
ACCESSIBILITY_REPORT,
|
ACCESSIBILITY_REPORT,
|
||||||
REPORTING,
|
REPORTING,
|
||||||
SENSITIVE_INFORMATION,
|
REPORTING_SIGNAL,
|
||||||
LOGGING,
|
REPORTING_PERIODISK_REDOVISNING,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export interface PeriodiskRedovisningActivityRequest {
|
||||||
|
id: number;
|
||||||
|
performedRemotely: boolean;
|
||||||
|
performedPhysically: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodiskRedovisningRequest {
|
||||||
|
genomforandeReferens: number;
|
||||||
|
period: string;
|
||||||
|
hasOfferedLanguageSupport: boolean;
|
||||||
|
hasOfferedJob: boolean;
|
||||||
|
activities: PeriodiskRedovisningActivityRequest[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
export interface PeriodiskRedovisningActivityResponse {
|
||||||
|
activityId: number;
|
||||||
|
activityName: string;
|
||||||
|
performedRemotely: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodiskRedovisningResponse {
|
||||||
|
genomforandeReferens: number;
|
||||||
|
period: string;
|
||||||
|
hasOfferedLanguageSupport: boolean;
|
||||||
|
hasOfferedJob: boolean;
|
||||||
|
activities: PeriodiskRedovisningActivityResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mockOnePeriodiskRedovisningResponse(): PeriodiskRedovisningResponse {
|
||||||
|
return {
|
||||||
|
genomforandeReferens: 100003857,
|
||||||
|
hasOfferedJob: false,
|
||||||
|
hasOfferedLanguageSupport: true,
|
||||||
|
period: '2021-10',
|
||||||
|
activities: [
|
||||||
|
{
|
||||||
|
activityId: 24,
|
||||||
|
activityName: 'Aktivitet 1',
|
||||||
|
performedRemotely: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activityId: 19,
|
||||||
|
activityName: 'Aktivitet 5',
|
||||||
|
performedRemotely: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
activityId: 31,
|
||||||
|
activityName: 'Aktivitet 5',
|
||||||
|
performedRemotely: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface AvropPeriod {
|
||||||
|
periodId: string;
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { PeriodiskRedovisningResponse } from './api/periodisk-redovisning.response.model';
|
||||||
|
|
||||||
|
export interface PeriodiskRedovisningActivity {
|
||||||
|
activityId: number;
|
||||||
|
activityName: string;
|
||||||
|
performedRemotely: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PeriodiskRedovisning {
|
||||||
|
genomforandeReferens: number;
|
||||||
|
period: string;
|
||||||
|
hasOfferedLanguageSupport: boolean;
|
||||||
|
hasOfferedJob: boolean;
|
||||||
|
activities: PeriodiskRedovisningActivity[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapResponseToPeriodiskRedovisning(data: PeriodiskRedovisningResponse): PeriodiskRedovisning {
|
||||||
|
const { genomforandeReferens, period, hasOfferedLanguageSupport, hasOfferedJob, activities } = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
genomforandeReferens,
|
||||||
|
period,
|
||||||
|
hasOfferedJob,
|
||||||
|
hasOfferedLanguageSupport,
|
||||||
|
activities,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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 { Params } from '@msfa-models/api/params.model';
|
||||||
|
import { PeriodiskRedovisningRequest } from '@msfa-models/api/periodisk-redovisning.request.model';
|
||||||
|
import {
|
||||||
|
mockOnePeriodiskRedovisningResponse,
|
||||||
|
PeriodiskRedovisningResponse,
|
||||||
|
} from '@msfa-models/api/periodisk-redovisning.response.model';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
|
import { mapResponseToPeriodiskRedovisning, PeriodiskRedovisning } from '@msfa-models/periodisk-redovisning.model';
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PeriodiskRedovisningApiService {
|
||||||
|
private _apiBaseUrl = `${environment.api.url}/rapporter/periodisk-redovisining`;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
public fetchPeriodiskRedovisning$(
|
||||||
|
periodStart: string,
|
||||||
|
periodEnd: string,
|
||||||
|
genomforandeReferens: number
|
||||||
|
): Observable<PeriodiskRedovisning> {
|
||||||
|
const params: Params = {
|
||||||
|
genomforandeReferens: genomforandeReferens.toString(),
|
||||||
|
periodStart,
|
||||||
|
periodEnd,
|
||||||
|
};
|
||||||
|
return of(mapResponseToPeriodiskRedovisning(mockOnePeriodiskRedovisningResponse()));
|
||||||
|
// return this.httpClient
|
||||||
|
// .get<{ data: PeriodiskRedovisningResponse }>(`${this._apiBaseUrl}`, { params })
|
||||||
|
// .pipe(map(({ data }) => (data ? mapResponseToPeriodiskRedovisning(data) : null)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public fetchAllPeriodiskaRedovisningar$(genomforandeReferens: number): Observable<PeriodiskRedovisning[]> {
|
||||||
|
return this.httpClient
|
||||||
|
.get<{ data: PeriodiskRedovisningResponse[] }>(`${this._apiBaseUrl}/${genomforandeReferens}`)
|
||||||
|
.pipe(map(({ data }) => data.map(pr => mapResponseToPeriodiskRedovisning(pr))));
|
||||||
|
}
|
||||||
|
|
||||||
|
public postPeriodiskRedovisning$(requestData: PeriodiskRedovisningRequest): Observable<void> {
|
||||||
|
return this.httpClient.post<void>(`${this._apiBaseUrl}`, requestData).pipe(
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte spara Periodisk redovisning.\n\n${error.message}`,
|
||||||
|
type: ErrorType.API,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,5 @@
|
|||||||
import { DateFormatOptions } from '@msfa-models/date-format-options.model';
|
import { DateFormatOptions } from '@msfa-models/date-format-options.model';
|
||||||
|
|
||||||
// Takes either 6 or 8 characters string (YYYYMMDD) and formats it to ISO standard (YYYY-MM-DD).
|
|
||||||
export function formatToIsoString(date: string): string {
|
|
||||||
if (date.length === 6) {
|
|
||||||
return `${date.substring(0, 4)}-${date.substring(4)}`;
|
|
||||||
} else if (date.length === 8) {
|
|
||||||
return `${date.substring(0, 4)}-${date.substring(4, 6)}-${date.substring(6)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatToDate(date: string): Date {
|
export function formatToDate(date: string): Date {
|
||||||
const year = date.substring(0, 4);
|
const year = date.substring(0, 4);
|
||||||
const month = date.substring(4, 6) || '01';
|
const month = date.substring(4, 6) || '01';
|
||||||
@@ -19,8 +8,8 @@ export function formatToDate(date: string): Date {
|
|||||||
return new Date(`${year}-${month}-${day}`);
|
return new Date(`${year}-${month}-${day}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dateToIsoString(date: Date, locale: string = 'sv-SE'): string {
|
export function formatDate(date: Date, locale: string = 'sv-SE', options?: DateFormatOptions): string {
|
||||||
const formatOptions: DateFormatOptions = {
|
const formatOptions: DateFormatOptions = options || {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'numeric',
|
month: 'numeric',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import { ValidationError } from '@msfa-models/validation-error.model';
|
|||||||
export function RequiredValidator(message = 'Fältet är obligatoriskt'): ValidatorFn {
|
export function RequiredValidator(message = 'Fältet är obligatoriskt'): ValidatorFn {
|
||||||
return (control: AbstractControl): ValidationError => {
|
return (control: AbstractControl): ValidationError => {
|
||||||
if (control) {
|
if (control) {
|
||||||
if (!control.value || (Array.isArray(control.value) && !control.value.length)) {
|
if (
|
||||||
|
control.value === null ||
|
||||||
|
control.value === undefined ||
|
||||||
|
(Array.isArray(control.value) && !control.value.length)
|
||||||
|
) {
|
||||||
return { required: message };
|
return { required: message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ export const ACTIVE_FEATURES_TEST: Feature[] = [
|
|||||||
Feature.AVROP,
|
Feature.AVROP,
|
||||||
Feature.REPORTING,
|
Feature.REPORTING,
|
||||||
Feature.LOGGING,
|
Feature.LOGGING,
|
||||||
Feature.SENSITIVE_INFORMATION,
|
Feature.DELTAGARE_SENSITIVE_INFORMATION,
|
||||||
Feature.RELEASES,
|
Feature.RELEASES,
|
||||||
Feature.VERSION_INFO,
|
Feature.VERSION_INFO,
|
||||||
|
Feature.REPORTING_PERIODISK_REDOVISNING,
|
||||||
];
|
];
|
||||||
|
|||||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mina-sidor-fa-web",
|
"name": "mina-sidor-fa-web",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "2.1.0",
|
"name": "mina-sidor-fa-web",
|
||||||
|
"version": "2.2.0",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -23742,6 +23743,11 @@
|
|||||||
"safer-buffer": "^2.0.2",
|
"safer-buffer": "^2.0.2",
|
||||||
"tweetnacl": "~0.14.0"
|
"tweetnacl": "~0.14.0"
|
||||||
},
|
},
|
||||||
|
"bin": {
|
||||||
|
"sshpk-conv": "bin/sshpk-conv",
|
||||||
|
"sshpk-sign": "bin/sshpk-sign",
|
||||||
|
"sshpk-verify": "bin/sshpk-verify"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -40478,7 +40484,9 @@
|
|||||||
"resolved": "http://nexus.arbetsformedlingen.se/repository/npm/ajv-formats/-/ajv-formats-2.1.0.tgz",
|
"resolved": "http://nexus.arbetsformedlingen.se/repository/npm/ajv-formats/-/ajv-formats-2.1.0.tgz",
|
||||||
"integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==",
|
"integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
"requires": {}
|
"requires": {
|
||||||
|
"ajv": "^8.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"alphanum-sort": {
|
"alphanum-sort": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user