feature(Avvikelserapporten): Använd nya dialogen (TV-845)

Merge in TEA/mina-sidor-fa-web from feature/TV-845-ersätt-dialogen-i-Avvikelserapporten-(TV-845) to develop

Squashed commit of the following:

commit 780f93baab0891a2f2ba49bf14d2c3add99e03c8
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Thu Dec 30 11:27:28 2021 +0100

    Update avvikelse-report-form.component.ts

commit bea883f6aedfe2655efbb69d66788c1366e48a43
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Thu Dec 30 11:19:03 2021 +0100

    cleanup

commit ffdf13dfcb3d97960ddb585b09fa5e6446504a68
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 15:52:37 2021 +0100

    Update app.component.ts

commit 1631cb763bc7023a9e95682272fb63dcbe15d84e
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 15:52:03 2021 +0100

    fix deltagarelist

commit c6080ac50cb6773aac8d4e45336fc1ba2f053a8d
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 15:24:44 2021 +0100

    Update avrop.component.html

commit 85057d0860ddceef8309253c983a1674a57291c4
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 15:21:33 2021 +0100

    wip

commit a73164bda8a8ae06c5700e382e197d823bef6767
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 15:20:04 2021 +0100

    wip

commit 83f3ada5c4c60c9e46d7b01bbbf92053eb1b29ff
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 15:18:30 2021 +0100

    wip

commit 5e184bc0e1a3f7bb6a3040d4da54a9b0e562dad3
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Wed Dec 29 14:18:39 2021 +0100

    wip

commit 30a90ce726dde31974e26c974215f6a5a60b025e
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Dec 28 15:11:14 2021 +0100

    added scrollbars inside dialog

commit 600dd20f3281b4206c12d851cba91ae0ecf1e21a
Merge: 7af19054 b9434741
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Dec 28 14:30:49 2021 +0100

    Merge branch 'develop' into feature/TV-845-ersätt-dialogen-i-Avvikelserapporten-(TV-845)

commit 7af190549c0109f71af87157e8099aa0483c879f
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Dec 28 12:05:22 2021 +0100

    wip

commit 6e47e4a641daf4cef121e96c3855e4ce4944c6c4
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Dec 28 09:38:36 2021 +0100

    wip

commit f9354d04f51425cce29f13a10b32555113edaaa2
Author: Daniel Appelgren <daniel.appelgren@arbetsformedlingen.se>
Date:   Tue Dec 28 09:16:54 2021 +0100

    feature(Nya deltagare): Använd nya dialogen (TV-845)
This commit is contained in:
Daniel Appelgren
2021-12-30 14:18:58 +01:00
parent 7c48ec175e
commit 8909f7c3d1
26 changed files with 359 additions and 142 deletions

View File

@@ -33,6 +33,7 @@ export class AppComponent extends UnsubscribeDirective implements OnInit {
private _dialogRef: UiDialogRef;
private _userIsIdle$: Observable<boolean> = this.idleService.isIdle$;
private _idleDialogConfig: UiDialogConfig = {
includeBasicFooter: true,
primaryButtonText: 'Fortsätt sessionen',
primaryAction: () => this.setUserAsActive(),
secondaryButtonText: 'Logga ut',

View File

@@ -1,32 +1,32 @@
<ng-container *ngIf="deleteEmployeeData$ | async as deleteEmployeeData">
<digi-ng-dialog
*ngIf="deleteEmployeeData.toDelete"
[afActive]="deleteEmployeeData.toDelete"
(afOnPrimaryClick)="deleteEmployeeModelPrimaryClick(deleteEmployeeData)"
(afOnSecondaryClick)="closeDeleteEmployeeModal()"
(afOnInactive)="closeDeleteEmployeeModal()"
afHeading="Ta bort personalkonto"
afHeadingLevel="h2"
[afPrimaryButtonText]="getPrimaryButtonText(deleteEmployeeData.lastDeleted)"
[afSecondaryButtonText]="getSecondaryButtonText(deleteEmployeeData.lastDeleted)"
>
<ng-container *ngIf="deleteEmployeeData.lastDeleted; else deletionWarning">
<digi-notification-alert af-variation="success" af-heading="Allt gick bra" af-heading-level="h3">
<p>Personalkonto för {{deleteEmployeeData.lastDeleted.fullName}} är borttaget.</p>
</digi-notification-alert>
</ng-container>
<ng-template #deletionWarning>
<p>Är du säker på att du vill ta bort personalkontot för {{deleteEmployeeData.toDelete.fullName}}?</p>
</ng-template>
<!-- <digi-ng-dialog-->
<!-- *ngIf="deleteEmployeeData.toDelete"-->
<!-- [afActive]="deleteEmployeeData.toDelete"-->
<!-- (afOnPrimaryClick)="deleteEmployeeModelPrimaryClick(deleteEmployeeData)"-->
<!-- (afOnSecondaryClick)="closeDeleteEmployeeModal()"-->
<!-- (afOnInactive)="closeDeleteEmployeeModal()"-->
<!-- afHeading="Ta bort personalkonto"-->
<!-- afHeadingLevel="h2"-->
<!-- [afPrimaryButtonText]="getPrimaryButtonText(deleteEmployeeData.lastDeleted)"-->
<!-- [afSecondaryButtonText]="getSecondaryButtonText(deleteEmployeeData.lastDeleted)"-->
<!-- >-->
<!-- <ng-container *ngIf="deleteEmployeeData.lastDeleted; else deletionWarning">-->
<!-- <digi-notification-alert af-variation="success" af-heading="Allt gick bra" af-heading-level="h3">-->
<!-- <p>Personalkonto för {{deleteEmployeeData.lastDeleted.fullName}} är borttaget.</p>-->
<!-- </digi-notification-alert>-->
<!-- </ng-container>-->
<!-- <ng-template #deletionWarning>-->
<!-- <p>Är du säker på att du vill ta bort personalkontot för {{deleteEmployeeData.toDelete.fullName}}?</p>-->
<!-- </ng-template>-->
<ng-container *ngIf="errorDuringDeletion$ | async as error">
<digi-notification-alert af-variation="danger" af-heading="Någonting gick fel" af-heading-level="h3">
<p>
Vi kunde inte radera personalkontot för {{deleteEmployeeData.toDelete.fullName}}. Ladda om sidan och försök
igen.
</p>
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
</digi-notification-alert>
</ng-container>
</digi-ng-dialog>
<!-- <ng-container *ngIf="errorDuringDeletion$ | async as error">-->
<!-- <digi-notification-alert af-variation="danger" af-heading="Någonting gick fel" af-heading-level="h3">-->
<!-- <p>-->
<!-- Vi kunde inte radera personalkontot för {{deleteEmployeeData.toDelete.fullName}}. Ladda om sidan och försök-->
<!-- igen.-->
<!-- </p>-->
<!-- <p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>-->
<!-- </digi-notification-alert>-->
<!-- </ng-container>-->
<!-- </digi-ng-dialog>-->
</ng-container>

View File

@@ -1,4 +1,3 @@
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@@ -7,7 +6,11 @@ import { EmployeeDeleteComponent } from './employee-delete.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [EmployeeDeleteComponent],
imports: [CommonModule, RouterModule, DigiNgDialogModule],
imports: [
CommonModule,
RouterModule,
// DigiNgDialogModule
],
exports: [EmployeeDeleteComponent],
})
export class EmployeeDeleteModule {}

View File

@@ -86,8 +86,6 @@
</digi-button>
<ng-template #informationDialog>
<h2>Information</h2>
<h3>Genomförandereferens</h3>
<p>
Genomförandereferens är det referensnummer du ska använda dig av i kontakten med Arbetsförmedlingen.
Du kan också använda genomförandereferensen till att leta fram en order i leverantörsportalen.
@@ -109,6 +107,7 @@
språkstöd som ingår i upphandlingen av olika tjänster och utbildningar. Du hittar mer information om
språkstöd och tolk i förfrågningsunderlaget för specifik upphandling.
</p>
<!-- </ui-dialog-layout>-->
</ng-template>
</div>
<msfa-avrop-list

View File

@@ -4,6 +4,7 @@ import { Handledare } from '@msfa-models/handledare.model';
import { AvropService } from '@msfa-services/avrop.service';
import { Observable } from 'rxjs';
import { UiDialog } from '@ui/dialog/ui-dialog.service';
import { UiDialogRef } from '@ui/dialog/ui-dialog-ref';
@Component({
selector: 'msfa-avrop',
@@ -13,6 +14,7 @@ import { UiDialog } from '@ui/dialog/ui-dialog.service';
})
export class AvropComponent implements OnDestroy {
@ViewChild('informationDialog') informationDialog: TemplateRef<unknown>;
uiDialogRef: UiDialogRef;
readonly totalAmountOfSteps = 3;
currentStep$: Observable<number> = this.avropService.currentStep$;
error$: Observable<string> = this.avropService.error$;
@@ -77,6 +79,6 @@ export class AvropComponent implements OnDestroy {
}
openAvropDialog(): void {
this.uiDialog.open(this.informationDialog);
this.uiDialogRef = this.uiDialog.open(this.informationDialog, { heading: 'Information', includeBasicFooter: true });
}
}

View File

@@ -9,7 +9,7 @@ import { UiSkeletonModule } from '@ui/skeleton/skeleton.module';
import { AvropComponent } from './avrop.component';
import { AvropFiltersModule } from './components/avrop-filters/avrop-filters.module';
import { AvropListModule } from './components/avrop-list/avrop-list.module';
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
import { UiDialogModule } from '@ui/dialog/ui-dialog.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -24,7 +24,7 @@ import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
UiLoaderModule,
HandledarePickerFormModule,
UnauthorizedAlertModule,
DigiNgDialogModule,
UiDialogModule,
],
})
export class AvropModule {}

View File

@@ -0,0 +1,30 @@
<ui-dialog-layout [isLoading]="submitIsLoading$ | async">
<h2 uiDialogHeading>Vill du skicka in Avvikelserapport (avvikelse)</h2>
<msfa-report-description-list [avrop]="data.avrop">
<dt>Orsak till avvikelse:</dt>
<dd>{{data.chosenReason.name }}</dd>
<ng-container *ngFor="let question of data.avvikelseSubmitData.avvikelseAlternativ.frageformular">
<dd>{{question.svar.length === 0 ? 'Inget svar' : question.svar }}</dd>
</ng-container>
<dt>Dag för avvikelse:</dt>
<dd>{{data.avvikelseSubmitData.avvikelseAlternativ.rapporteringsdatum }}</dd>
</msfa-report-description-list>
<digi-notification-alert
*ngIf="submitError$ | async as error"
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>
<ng-container uiDialogFooter>
<digi-button af-type="button" (click)="submitAndCloseConfirmDialog(data.avvikelseSubmitData)"
>Skicka in</digi-button
>
<digi-button af-type="button" af-variation="secondary" (click)="close()"> Avbryt </digi-button>
</ng-container>
</ui-dialog-layout>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AvvikelseConfirmDialogComponent } from './avvikelse-confirm-dialog.component';
describe('AvvikelseConfirmDialogComponent', () => {
let component: AvvikelseConfirmDialogComponent;
let fixture: ComponentFixture<AvvikelseConfirmDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AvvikelseConfirmDialogComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AvvikelseConfirmDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,49 @@
import { Component } from '@angular/core';
import { UiDialogRef } from '@ui/dialog/ui-dialog-ref';
import { DeltagareAvrop } from '@msfa-models/avrop.model';
import { AvvikelseReason } from '@msfa-models/avvikelse-reason.model';
import { AvvikelseReportRequest } from '@msfa-models/api/avvikelse-request.model';
import { AvvikelseReportFormService } from '../avvikelse-report-form.service';
import { CustomError } from '@msfa-models/error/custom-error';
import { BehaviorSubject } from 'rxjs';
export interface AvvikelseConfirmDialogData {
avrop: DeltagareAvrop;
chosenReason: AvvikelseReason;
avvikelseSubmitData: AvvikelseReportRequest;
}
@Component({
selector: 'msfa-avvikelse-confirm-dialog',
templateUrl: './avvikelse-confirm-dialog.component.html',
styleUrls: ['./avvikelse-confirm-dialog.component.scss'],
})
export class AvvikelseConfirmDialogComponent {
submitIsLoading$ = new BehaviorSubject<boolean>(false);
submitError$ = new BehaviorSubject<CustomError>(null);
constructor(public uiDialogRef: UiDialogRef, private avvikelseReportFormService: AvvikelseReportFormService) {}
get data(): AvvikelseConfirmDialogData {
return this.uiDialogRef.config.data as AvvikelseConfirmDialogData;
}
close(): void {
this.uiDialogRef.close();
}
submitAndCloseConfirmDialog(avvikelseSubmitData: AvvikelseReportRequest): void {
this.submitIsLoading$.next(true);
this.avvikelseReportFormService.createAvvikelse$(avvikelseSubmitData).subscribe({
next: () => {
this.submitIsLoading$.next(false);
this.uiDialogRef.close({ submitted: new Date() });
},
error: (customError: CustomError) => {
this.submitError$.next({ ...customError, message: customError.error.message });
this.submitIsLoading$.next(false);
throw { ...customError, avoidToast: true };
},
});
}
}

View File

@@ -33,7 +33,11 @@
</div>
<ng-template #formRef>
<form class="avvikelse-report-form__form" [formGroup]="avvikelseFormGroup" (ngSubmit)="openConfirmDialog()">
<form
class="avvikelse-report-form__form"
[formGroup]="avvikelseFormGroup"
(ngSubmit)="openConfirmDialog(avrop)"
>
<div class="avvikelse-report-form__form-item">
<ui-select
*ngIf="reasonsAsUiSelectOptions$ | async as reason; else loadingRef"
@@ -90,40 +94,15 @@
</ui-link-button>
</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"
<digi-notification-alert
*ngIf="submitError$ | async as error"
af-variation="danger"
af-heading="Någonting gick fel"
>
<ui-loader *ngIf="submitIsLoading$ | async" uiType="absolute"></ui-loader>
<msfa-report-description-list [avrop]="avrop">
<dt>Orsak till avvikelse:</dt>
<dd>{{(chosenReason$ | async)?.name }}</dd>
<ng-container *ngIf="avvikelseSubmitData$ | async as 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>
<p>Kunde inte spara Avvikelserapport (avvikelse). Försök igen om en stund.</p>
<p *ngIf="error.message" class="msfa__small-text">{{error.message}}</p>
</digi-notification-alert>
</ng-template>
</ng-template>
</div>

View File

@@ -12,8 +12,14 @@ 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 { first, map, shareReplay, switchMap, tap } from 'rxjs/operators';
import { AvvikelseReportFormService } from './avvikelse-report-form.service';
import { UiDialog } from '@ui/dialog/ui-dialog.service';
import { UiDialogRef } from '@ui/dialog/ui-dialog-ref';
import {
AvvikelseConfirmDialogComponent,
AvvikelseConfirmDialogData,
} from './avvikelse-confirm-dialog/avvikelse-confirm-dialog.component';
interface Params {
genomforandeReferens: string;
@@ -34,20 +40,22 @@ type AvvikelseFormKeys = keyof AvvikelseFormData;
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
confirmDialogRef: UiDialogRef;
shouldValidate$ = new BehaviorSubject<boolean>(false);
reasonFormName: AvvikelseFormKeys = 'reason';
questionsFormName: AvvikelseFormKeys = 'questions';
reportingDateFormName: AvvikelseFormKeys = 'reportingDate';
submitIsLoading$ = new BehaviorSubject<boolean>(false);
submitError$ = new BehaviorSubject<CustomError>(null);
genomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
map((params: Params) => +params.genomforandeReferens)
);
avrop: DeltagareAvrop;
avrop$: Observable<DeltagareAvrop> = this.genomforandeReferens$.pipe(
switchMap(genomforandeReferens => this.avvikelseReportFormService.fetchAvropInformation$(genomforandeReferens)),
tap(avrop => (this.avrop = avrop)),
shareReplay(1)
);
@@ -60,8 +68,6 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
chosenReasonId$: Observable<string>;
chosenReason$: Observable<AvvikelseReason>;
questionsForChosenReason$: Observable<AvvikelseQuestion[]>;
avvikelseSubmitData$: Observable<AvvikelseReportRequest>;
confirmDialogIsOpen$ = new BehaviorSubject<boolean>(false);
submittedDate$ = new BehaviorSubject<Date | null>(null);
private subscriptions: Subscription[] = [];
private todayDateISO = new Date().toISOString().slice(0, 10);
@@ -70,11 +76,19 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
[this.reportingDateFormName]: new FormControl(this.todayDateISO, [RequiredValidator('Datum är obligatoriskt')]),
[this.questionsFormName]: new FormArray([]),
});
private formData$: Observable<AvvikelseFormData> = this.avvikelseFormGroup
.valueChanges as Observable<AvvikelseFormData>;
private currentQuestions: AvvikelseQuestion[];
constructor(private avvikelseReportFormService: AvvikelseReportFormService, private activatedRoute: ActivatedRoute) {}
constructor(
private avvikelseReportFormService: AvvikelseReportFormService,
private activatedRoute: ActivatedRoute,
private uiDialog: UiDialog
) {
this.chosenReasonId$ = this.reasonFormControl.valueChanges as Observable<string>;
this.chosenReason$ = combineLatest([this.chosenReasonId$, this.reasons$]).pipe(
map(([chosenReasonId, reasons]) => reasons.find(reason => reason.id.toString() === chosenReasonId)),
shareReplay(1)
);
}
get reasonFormControl(): AbstractControl | undefined {
return this.avvikelseFormGroup.get(this.reasonFormName);
@@ -93,11 +107,6 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this.chosenReasonId$ = this.reasonFormControl.valueChanges as Observable<string>;
this.chosenReason$ = combineLatest([this.chosenReasonId$, this.reasons$]).pipe(
map(([chosenReasonId, reasons]) => reasons.find(reason => reason.id.toString() === chosenReasonId))
);
this.questionsForChosenReason$ = combineLatest([this.chosenReasonId$, this.allAvvikelseQuestions$]).pipe(
map(([chosenOrsak, allAvvikelseQuestions]) => {
return allAvvikelseQuestions.filter(question => question.id.startsWith(chosenOrsak.toString() + '_'));
@@ -105,21 +114,15 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
);
this.subscriptions.push(
this.chosenReason$.subscribe(() => {
this.chosenReason$.subscribe(chosenReason => {
this.shouldValidate$.next(false);
}),
this.questionsForChosenReason$.subscribe(questions => {
this.clearQuestions();
questions.forEach(question => this.addQuestionToForm(question));
})
);
this.avvikelseSubmitData$ = combineLatest([this.genomforandeReferens$, this.chosenReasonId$, this.formData$]).pipe(
map(([genomforandeReferens, chosenReason, formData]) =>
this.makeAvvikelseSubmitData(genomforandeReferens, chosenReason, formData)
),
shareReplay(1)
);
}
questionIsRequired(question: AvvikelseQuestion): boolean {
@@ -138,6 +141,7 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
private _isAfterStartDate(startDate: Date): boolean {
return new Date() > startDate;
}
private _isBeforeLastPossibleReportDay(endDate: Date): boolean {
// Reporting is allowed at latest 5 days past avrop end date.
// Because it's workdays and not calendar days we temporarily set this to much more. This date should be fetched from API in the future
@@ -158,44 +162,41 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
}
}
openConfirmDialog(): void {
openConfirmDialog(avrop: DeltagareAvrop): void {
this.shouldValidate$.next(true);
markControlsAsDirty(Object.values(this.avvikelseFormGroup.controls));
this.avvikelseFormGroup.markAllAsTouched();
if (this.avvikelseFormGroup.valid) {
this.confirmDialogIsOpen$.next(true);
combineLatest([this.chosenReason$, this.genomforandeReferens$])
.pipe(
first(),
switchMap(([chosenReason, genomforandeReferens]) => {
const avvikelseSubmitData: AvvikelseReportRequest = this._makeAvvikelseSubmitData(
genomforandeReferens,
chosenReason.id.toString(),
this.avvikelseFormGroup.value as AvvikelseFormData
);
const data: AvvikelseConfirmDialogData = { chosenReason, avvikelseSubmitData, avrop };
return this.uiDialog.open<{ submitted: Date }>(AvvikelseConfirmDialogComponent, { data }).afterClosed$;
})
)
.subscribe(closedResult => {
this.submitError$.next(null);
if (closedResult.data?.submitted) {
this.submittedDate$.next(closedResult.data.submitted);
}
});
}
}
submitAndCloseConfirmDialog(): void {
this.submitIsLoading$.next(true);
this.avvikelseSubmitData$.pipe(take(1)).subscribe(avvikelseSubmitData =>
this.avvikelseReportFormService.createAvvikelse$(avvikelseSubmitData).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 };
},
})
);
}
cancelConfirmDialog(): void {
this.confirmDialogIsOpen$.next(false);
this.submitError$.next(null);
}
ngOnDestroy(): void {
this.subscriptions.forEach(subscription => subscription.unsubscribe());
}
private makeAvvikelseSubmitData(
private _makeAvvikelseSubmitData(
genomforandeReferens: number,
chosenReason: string,
formData: AvvikelseFormData
@@ -219,6 +220,8 @@ export class AvvikelseReportFormComponent implements OnInit, OnDestroy {
private addQuestionToForm(question: AvvikelseQuestion): void {
// FormArray doesnt hold any IDs so we need to store these seperately and rebuild structure at submit
// TODO we can actually just put id in the formgroup, as we do in slutredovisning-form-step1.component.ts. That would simplify this file.
this.currentQuestions.push(question);
this.questionsFormArray.push(

View File

@@ -1,4 +1,3 @@
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
@@ -17,10 +16,12 @@ import { ReportDescriptionListModule } from '../../../components/report-descript
import { ReportLayoutModule } from '../../../components/report-layout/report-layout.module';
import { AvvikelseReportFormComponent } from './avvikelse-report-form.component';
import { AvvikelseReportFormService } from './avvikelse-report-form.service';
import { AvvikelseConfirmDialogComponent } from './avvikelse-confirm-dialog/avvikelse-confirm-dialog.component';
import { UiDialogModule } from '@ui/dialog/ui-dialog.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [AvvikelseReportFormComponent],
declarations: [AvvikelseReportFormComponent, AvvikelseConfirmDialogComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: AvvikelseReportFormComponent }]),
@@ -34,10 +35,10 @@ import { AvvikelseReportFormService } from './avvikelse-report-form.service';
UiLoaderModule,
UiSelectModule,
ReportDescriptionListModule,
DigiNgDialogModule,
UiTextareaModule,
UiLinkButtonModule,
PreventDoubleSubmitModule,
UiDialogModule,
],
providers: [AvvikelseReportFormService],
exports: [AvvikelseReportFormComponent],

View File

@@ -1,5 +1,3 @@
<h2>Händelser för {{deltagare.fullName}}</h2>
<div class="deltagare-list-handelser" *ngIf="deltagare">
<h3 *ngIf="activeHandelseMotivation" class="deltagare-list-handelser__sub-heading">{{deltagare.fullName}}</h3>
<p>Genomförandereferens: <strong>{{deltagare.genomforandeReferens}}</strong></p>

View File

@@ -85,7 +85,9 @@ export class DeltagareListTableComponent {
openHandelser(singleDeltagare: DeltagareCompact): void {
this.uiDialog.open(this.handelserDialogComponent, {
data: singleDeltagare,
heading: 'Händelser för ' + singleDeltagare.fullName,
primaryButtonText: 'Stäng',
includeBasicFooter: true,
});
}

View File

@@ -0,0 +1,14 @@
<div class="ui-dialog-layout">
<ui-loader *ngIf="isLoading" uiType="absolute"></ui-loader>
<div class="ui-dialog-layout__heading">
<ng-content select="[uiDialogHeading]"></ng-content>
</div>
<div class="ui-dialog-layout__scrollable-content">
<ng-content></ng-content>
</div>
<footer class="ui-dialog-layout__footer">
<ng-content select="[uiDialogFooter]"></ng-content>
</footer>
</div>

View File

@@ -0,0 +1,40 @@
@import 'variables/shadows';
@import 'variables/gutters';
.ui-dialog-layout {
padding-top: $digi--layout--gutter--s;
display: flex;
flex-direction: column;
gap: $digi--layout--gutter--s;
max-height: 90vh;
&__heading {
flex: 1 1 2rem;
}
&__scrollable-content {
overflow: auto;
flex-grow: 1;
}
&__close-button {
position: absolute;
top: var(--digi--layout--gutter);
right: var(--digi--layout--gutter--s);
background: transparent;
border: none;
display: flex;
justify-content: center;
align-items: center;
}
&__close-button-text {
font-size: var(--digi--typography--font-size--s);
}
&__footer {
flex: 1 0 4rem;
min-height: var(--digi--layout--gutter);
display: flex;
margin-top: $digi--layout--gutter--l;
gap: var(--digi--layout--gutter);
}
}

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UiDialogLayoutComponent } from './ui-dialog-layout.component';
describe('UiDialogLayoutComponent', () => {
let component: UiDialogLayoutComponent;
let fixture: ComponentFixture<UiDialogLayoutComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UiDialogLayoutComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(UiDialogLayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,11 @@
import { Component, Input } from '@angular/core';
@Component({
selector: 'ui-dialog-layout',
templateUrl: './ui-dialog-layout.component.html',
styleUrls: ['./ui-dialog-layout.component.scss'],
})
export class UiDialogLayoutComponent {
@Input() uiHeading: string;
@Input() isLoading = false;
}

View File

@@ -21,12 +21,18 @@ export class UiDialogRef<CloseResponseData = unknown, InputDataType = unknown> {
overlay.backdropClick().subscribe(() => this._close('backdropClick', null));
}
get includeBasicFooter(): boolean {
return this.config.includeBasicFooter;
}
get primaryButtonText(): string {
return this.config.primaryButtonText;
}
get secondaryButtonText(): string {
return this.config.secondaryButtonText;
}
get heading(): string {
return this.config.heading;
}
close(data?: CloseResponseData): void {
this._close('close', data);

View File

@@ -1,21 +1,25 @@
<div class="ui-dialog">
<ng-container [ngSwitch]="contentType">
<ng-container *ngSwitchCase="'string'">
<div class="box">
<h2 class="ui-dialog__heading" *ngIf="heading">{{heading}}</h2>
<div [ngClass]="{'ui-dialog__scrollable-content': includeBasicFooter}">
<ng-container [ngSwitch]="contentType">
<ng-container *ngSwitchCase="'string'">
<div [innerHTML]="content"></div>
</div>
</ng-container>
<footer class="ui-dialog__footer">
<digi-button af-type="button" (click)="close()">Stäng</digi-button>
</footer>
</ng-container>
<ng-container *ngSwitchCase="'template'">
<ng-container *ngTemplateOutlet="content; context: context"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'template'">
<ng-container *ngTemplateOutlet="content; context: context"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'component'">
<ng-container *ngComponentOutlet="content"></ng-container>
<ng-container *ngSwitchCase="'component'">
<ng-container *ngComponentOutlet="content"></ng-container>
</ng-container>
</ng-container>
</ng-container>
</div>
<footer class="ui-dialog__footer">
<footer class="ui-dialog__footer" *ngIf="includeBasicFooter">
<digi-button af-type="button" (click)="primaryAction()">{{primaryButtonText}}</digi-button>
<digi-button af-type="button" af-variation="secondary" *ngIf="secondaryButtonText" (click)="secondaryAction()">
{{secondaryButtonText}}

View File

@@ -7,6 +7,18 @@
box-shadow: $msfa__shadow;
padding: $digi--layout--gutter--s $digi--layout--gutter--xl $digi--layout--gutter--l;
position: relative;
max-height: 90vh;
display: flex;
flex-direction: column;
&__heading {
flex: 1 1 2rem;
}
&__scrollable-content {
overflow: auto;
flex-grow: 1;
}
&__close-button {
position: absolute;

View File

@@ -28,11 +28,18 @@ export class UiDialogComponent implements OnInit {
get secondaryButtonText(): string {
return this.uiDialogRef.secondaryButtonText;
}
get heading(): string {
return this.uiDialogRef.heading;
}
primaryAction(): void {
this.uiDialogRef.primaryAction();
}
get includeBasicFooter(): boolean {
return this.uiDialogRef.includeBasicFooter;
}
secondaryAction(): void {
this.uiDialogRef.secondaryAction();
}

View File

@@ -9,9 +9,12 @@ export interface UiDialogConfig<DialogInputData = unknown> {
minHeight?: string;
maxHeight?: string;
heading?: string;
/**
* primaryButtonText defaults to 'Stäng'
*/
includeBasicFooter?: boolean;
primaryButtonText?: string;
/**

View File

@@ -3,11 +3,14 @@ import { CommonModule } from '@angular/common';
import { UiDialogComponent } from './ui-dialog.component';
import { UiIconModule } from '@ui/icon/icon.module';
import { UiDialog } from '@ui/dialog/ui-dialog.service';
import { UiDialogLayoutComponent } from './ui-dialog-layout/ui-dialog-layout.component';
import { UiLoaderModule } from '@ui/loader/loader.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [UiDialogComponent],
imports: [CommonModule, UiIconModule],
declarations: [UiDialogComponent, UiDialogLayoutComponent],
imports: [CommonModule, UiIconModule, UiLoaderModule],
providers: [UiDialog],
exports: [UiDialogLayoutComponent],
})
export class UiDialogModule {}

View File

@@ -19,24 +19,24 @@ export class UiDialog {
});
}
open<DialogContent = unknown, T = unknown>(
open<CloseResponseData = unknown, InputDataType = unknown>(
content: string | TemplateRef<unknown> | Type<unknown>,
config: UiDialogConfig<T> = {}
): UiDialogRef<DialogContent> {
config: UiDialogConfig<InputDataType> = { includeBasicFooter: false }
): UiDialogRef<CloseResponseData> {
const positionStrategy = this.overlay.position().global().centerHorizontally().centerVertically();
const configs = new OverlayConfig({
positionStrategy,
minWidth: config.minWidth ?? '40rem',
minHeight: config.minHeight ?? '40rem',
minWidth: config.minWidth ?? '20rem',
minHeight: config.minHeight ?? '10rem',
maxWidth: config.maxWidth ?? '60rem',
maxHeight: config.maxHeight ?? '60rem',
hasBackdrop: true,
scrollStrategy: this.overlay.scrollStrategies.close(),
scrollStrategy: this.overlay.scrollStrategies.block(),
backdropClass: 'cdk-overlay-dark-backdrop',
});
const overlayRef = this.overlay.create(configs);
const uiDialogRef = new UiDialogRef<DialogContent, T>(overlayRef, content, config);
const uiDialogRef = new UiDialogRef<CloseResponseData, InputDataType>(overlayRef, content, config);
const injector = UiDialog._createInjector(uiDialogRef, this.injector, config);
overlayRef.attach(new ComponentPortal(UiDialogComponent, null, injector));