diff --git a/.eslintrc.json b/.eslintrc.json
index d7e458c..98e0e4c 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -11,9 +11,7 @@
{
"enforceBuildableLibDependency": true,
"allow": [],
- "depConstraints": [
- { "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] }
- ]
+ "depConstraints": [{ "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"] }]
}
]
}
diff --git a/apps/mina-sidor-fa/.eslintrc.json b/apps/mina-sidor-fa/.eslintrc.json
index f602bc2..9695e63 100644
--- a/apps/mina-sidor-fa/.eslintrc.json
+++ b/apps/mina-sidor-fa/.eslintrc.json
@@ -14,6 +14,15 @@
],
"parserOptions": { "project": ["apps/mina-sidor-fa/tsconfig.*?.json"] },
"rules": {
+ "max-len": [
+ 1,
+ 140,
+ 2,
+ {
+ "ignorePattern": "^import\\s.+\\sfrom\\s.+;$",
+ "ignoreUrls": true
+ }
+ ],
"@angular-eslint/directive-selector": [
"error",
{
diff --git a/apps/mina-sidor-fa/src/app/app-routing.module.ts b/apps/mina-sidor-fa/src/app/app-routing.module.ts
index 66dd96a..5795781 100644
--- a/apps/mina-sidor-fa/src/app/app-routing.module.ts
+++ b/apps/mina-sidor-fa/src/app/app-routing.module.ts
@@ -42,7 +42,7 @@ activeFeatures.forEach(feature => {
case Feature.ADMINISTRATION:
routes.push({
path: 'administration',
- data: { title: 'Administration', expectedRole: RoleEnum.MSFA_AuthAdmin },
+ data: { title: 'Administration', expectedRoles: [RoleEnum.MSFA_AuthAdmin] },
loadChildren: () => import('./pages/administration/administration.module').then(m => m.AdministrationModule),
canActivate: [AuthGuard, OrganizationGuard, RoleGuard],
});
@@ -50,7 +50,7 @@ activeFeatures.forEach(feature => {
case Feature.AVROP:
routes.push({
path: 'nya-deltagare',
- data: { title: 'Nya deltagare', expectedRole: RoleEnum.MSFA_ReceiveDeltagare },
+ data: { title: 'Nya deltagare', expectedRoles: [RoleEnum.MSFA_ReceiveDeltagare] },
loadChildren: () => import('./pages/avrop/avrop.module').then(m => m.AvropModule),
canActivate: [AuthGuard, OrganizationGuard, RoleGuard],
});
@@ -58,7 +58,7 @@ activeFeatures.forEach(feature => {
case Feature.DELTAGARE:
routes.push({
path: 'deltagare',
- data: { title: 'Deltagare', expectedRole: RoleEnum.MSFA_ReportAndPlanning },
+ data: { title: 'Deltagare', expectedRoles: [RoleEnum.MSFA_ReportAndPlanning, RoleEnum.MSFA_ReceiveDeltagare] },
loadChildren: () => import('./pages/deltagare/deltagare.module').then(m => m.DeltagareModule),
canActivate: [AuthGuard, OrganizationGuard, RoleGuard],
});
diff --git a/apps/mina-sidor-fa/src/app/app.component.ts b/apps/mina-sidor-fa/src/app/app.component.ts
index 80927f4..4ea91e8 100644
--- a/apps/mina-sidor-fa/src/app/app.component.ts
+++ b/apps/mina-sidor-fa/src/app/app.component.ts
@@ -1,9 +1,9 @@
import { DOCUMENT } from '@angular/common';
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
-import { environment } from '@msfa-environment';
-import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
-import { filter, map, mergeMap, switchMap } from 'rxjs/operators';
import { Title } from '@angular/platform-browser';
+import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
+import { environment } from '@msfa-environment';
+import { filter, map, switchMap } from 'rxjs/operators';
@Component({
selector: 'msfa-root',
diff --git a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss
index 7fde7dc..0e35d60 100644
--- a/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss
+++ b/apps/mina-sidor-fa/src/app/components/toast-list/toast/toast.component.scss
@@ -50,6 +50,7 @@
max-width: 400px !important;
margin: 0;
overflow-wrap: break-word;
+ white-space: pre-wrap;
}
&__close-button {
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare.component.ts
index e1cf5dd..cdd18c2 100644
--- a/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare.component.ts
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/deltagare.component.ts
@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DeltagareCompact, DeltagareCompactData } from '@msfa-models/deltagare.model';
import { Sort } from '@msfa-models/sort.model';
-import { DeltagareService } from '@msfa-services/api/deltagare.service';
+import { DeltagareService } from '@msfa-services/deltagare.service';
import { Observable } from 'rxjs';
@Component({
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.html
new file mode 100644
index 0000000..91be9e1
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.html
@@ -0,0 +1,83 @@
+
+
+
Arbetslivserfarenhet
+
+
+
+
+
+ {{ accordionExpanded ? 'Dölj' : 'Visa' }} fler arbetsgivare
+
+
+
+
+
+
Utbildning
+
+ - Högsta utbildningsnivå:
+ -
+
+ {{ highestEducation.level.description }}: {{ highestEducation.sunKod.description }}
+
+
+
+
+
+ Utbildningar:
+
+
+
+
+
+
+
Körkort
+
+ - Har körkort
+ - {{driversLicense.licenses.length ? 'Ja' : 'Nej'}}
+
+ - Körkortsklasser
+ - {{driversLicense.licenses.join(', ')}}
+ - Tillgång till bil
+ - {{driversLicense.accessToCar ? 'Ja' : 'Nej'}}
+
+
+
+
+
+
+ Info saknas
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.scss
new file mode 100644
index 0000000..fb6b80c
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.scss
@@ -0,0 +1,34 @@
+@import 'mixins/list';
+
+.deltagare-tab-experiences {
+ display: contents;
+
+ &__tab-column {
+ flex-grow: 1;
+ flex-basis: 0;
+ }
+
+ &__subheading {
+ font-size: var(--digi--typography--font-size--desktop);
+ font-weight: var(--digi--typography--font-weight--semibold);
+ margin: var(--digi--layout--gutter--s) 0 0;
+
+ &--with-margin {
+ font-size: var(--digi--typography--font-size--h3);
+ margin-bottom: var(--digi--layout--gutter--s);
+ }
+ }
+
+ &__experience-list {
+ @include msfa__reset-list;
+ }
+
+ &__accordion {
+ display: block;
+ margin-top: var(--digi--layout--gutter);
+ }
+
+ &__accordion-trigger {
+ font-weight: var(--digi--typography--font-weight--semibold);
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.spec.ts
new file mode 100644
index 0000000..42d612f
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { DeltagareTabExperiencesComponent } from './deltagare-tab-experiences.component';
+
+describe('DeltagareTabExperiencesComponent', () => {
+ let component: DeltagareTabExperiencesComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [DeltagareTabExperiencesComponent],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DeltagareTabExperiencesComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.ts
new file mode 100644
index 0000000..771858b
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-experiences/deltagare-tab-experiences.component.ts
@@ -0,0 +1,32 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { DriversLicense } from '@msfa-models/drivers-license.model';
+import { Education } from '@msfa-models/education.model';
+import { HighestEducation } from '@msfa-models/highest-education.model';
+import { WorkExperience } from '@msfa-models/work-experience.model';
+
+@Component({
+ selector: 'msfa-deltagare-tab-experiences',
+ templateUrl: './deltagare-tab-experiences.component.html',
+ styleUrls: ['./deltagare-tab-experiences.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DeltagareTabExperiencesComponent {
+ @Input() workExperiences: WorkExperience[];
+ @Input() highestEducation: HighestEducation;
+ @Input() educations: Education[];
+ @Input() driversLicense: DriversLicense;
+
+ accordionExpanded = false;
+
+ get firstVisibleWorkExperiences(): WorkExperience[] {
+ return this.workExperiences?.slice(0, 2);
+ }
+
+ get hiddenWorkExperiences(): WorkExperience[] {
+ return this.workExperiences?.slice(2);
+ }
+
+ toggleAccordionExpanded(): void {
+ this.accordionExpanded = !this.accordionExpanded;
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.html
new file mode 100644
index 0000000..0407d47
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.html
@@ -0,0 +1,76 @@
+
+
+
+ Info saknas
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.scss
new file mode 100644
index 0000000..1d96ee1
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.scss
@@ -0,0 +1,8 @@
+.deltagare-tab-personal-information {
+ display: contents;
+
+ &__tab-column {
+ flex-grow: 1;
+ flex-basis: 0;
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.spec.ts
new file mode 100644
index 0000000..060e099
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { DeltagareTabPersonalInformationComponent } from './deltagare-tab-personal-information.component';
+
+describe('DeltagareTabPersonalInformationComponent', () => {
+ let component: DeltagareTabPersonalInformationComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [DeltagareTabPersonalInformationComponent],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DeltagareTabPersonalInformationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.ts
new file mode 100644
index 0000000..ba5b9b9
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-personal-information/deltagare-tab-personal-information.component.ts
@@ -0,0 +1,14 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { Avrop } from '@msfa-models/avrop.model';
+import { ContactInformation } from '@msfa-models/contact-information.model';
+
+@Component({
+ selector: 'msfa-deltagare-tab-personal-information',
+ templateUrl: './deltagare-tab-personal-information.component.html',
+ styleUrls: ['./deltagare-tab-personal-information.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DeltagareTabPersonalInformationComponent {
+ @Input() contactInformation: ContactInformation;
+ @Input() avropInformation: Avrop;
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.html
new file mode 100644
index 0000000..5de91c8
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
Inskickade rapporter
+
+
+
+
+
+
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.scss
new file mode 100644
index 0000000..51ed0bc
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.scss
@@ -0,0 +1,12 @@
+@import 'variables/gutters';
+
+.deltagare-tab-reports {
+ &__form {
+ max-width: var(--digi--typography--text--max-width);
+ }
+
+ &__cta-wrapper {
+ margin-top: $digi--layout--gutter--l;
+ margin-bottom: $digi--layout--gutter--xl;
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.spec.ts
new file mode 100644
index 0000000..7f10ba0
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.spec.ts
@@ -0,0 +1,28 @@
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
+import { DeltagareTabReportsComponent } from './deltagare-tab-reports.component';
+
+describe('DeltagareTabReportsComponent', () => {
+ let component: DeltagareTabReportsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [DeltagareTabReportsComponent],
+ imports: [RouterTestingModule, HttpClientTestingModule, ReactiveFormsModule, LoaderModule],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DeltagareTabReportsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.ts
new file mode 100644
index 0000000..6a8142b
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-reports/deltagare-tab-reports.component.ts
@@ -0,0 +1,92 @@
+import { FormSelectItem } from '@af/digi-ng/_form/form-select';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ReportsData } from '@msfa-models/reports.model';
+import { DeltagareCardService } from '@msfa-services/deltagare-card.service';
+import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
+import { distinctUntilChanged, map, shareReplay, switchMap } from 'rxjs/operators';
+
+@Component({
+ selector: 'msfa-deltagare-tab-reports',
+ templateUrl: './deltagare-tab-reports.component.html',
+ styleUrls: ['./deltagare-tab-reports.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DeltagareTabReportsComponent {
+ private _limit$ = new BehaviorSubject(20);
+ private _page$ = new BehaviorSubject(1);
+ private _type$ = new BehaviorSubject(null);
+ readonly reportsFormControlName = 'reports';
+
+ public currentDeltagareId$: Observable = this.activatedRoute.params.pipe(
+ map(params => params.deltagareId as string),
+ distinctUntilChanged(([prevDeltagareId], [currDeltagareId]) => prevDeltagareId === currDeltagareId)
+ );
+
+ reportsData$: Observable = combineLatest([this.currentDeltagareId$, this._limit$, this._page$]).pipe(
+ switchMap(([deltagareId, limit, page]) => this.deltagareCardService.fetchReports$(limit, page, deltagareId)),
+ shareReplay(1)
+ );
+
+ reportPickerFormGroup: FormGroup = this.formBuilder.group({
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ reports: this.formBuilder.control('', [Validators.required]),
+ });
+
+ selectableReportTypes: Array = [
+ { name: 'Avvikelse', value: 'avvikelse' },
+ { name: 'Gemensam Planering', value: 'planering' },
+ ];
+ selectedReportType: FormSelectItem;
+
+ constructor(
+ private activatedRoute: ActivatedRoute,
+ private deltagareCardService: DeltagareCardService,
+ private formBuilder: FormBuilder,
+ private router: Router
+ ) {}
+
+ get reportsFormControl(): AbstractControl | null {
+ return this.reportPickerFormGroup?.get(this.reportsFormControlName);
+ }
+
+ onFormSubmitted(event: Event, reportType: string, deltagareId: string): void {
+ event.preventDefault();
+
+ switch (reportType) {
+ case 'planering':
+ if (this.reportsFormControl.valid) {
+ this.router.navigate(['/deltagare/planering', deltagareId]).catch(error => {
+ console.error(error);
+ });
+ }
+ break;
+ case 'avvikelse':
+ if (this.reportsFormControl.valid) {
+ this.router.navigate(['/deltagare/rapportera', deltagareId]).catch(error => {
+ console.error(error);
+ });
+ }
+ break;
+ default:
+ return;
+ }
+
+ this.reportsFormControl.markAsTouched();
+
+ if (!this.selectableReportTypes || this.reportPickerFormGroup.invalid) {
+ return;
+ }
+
+ const selectedReportType = this.selectableReportTypes.find(report => {
+ return report.value === this.reportsFormControl.value;
+ });
+
+ this._type$.next(selectedReportType);
+ }
+
+ setNewPage(page: number): void {
+ this._page$.next(page);
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.html
new file mode 100644
index 0000000..1835596
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.html
@@ -0,0 +1,36 @@
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.scss
new file mode 100644
index 0000000..ed7bcc5
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.scss
@@ -0,0 +1,19 @@
+@import 'variables/z-index';
+
+.deltagare-tab-sensitive-information {
+ display: contents;
+
+ &__tab-column {
+ flex-grow: 1;
+ flex-basis: 0;
+ }
+
+ &__popover {
+ display: inline-block;
+ margin-left: var(--digi--layout--gutter--s);
+
+ ::ng-deep .digi-ng-popover__container {
+ z-index: $msfa__z-index-popover;
+ }
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.spec.ts
new file mode 100644
index 0000000..2b7dd07
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DeltagareTabSensitiveInformationComponent } from './deltagare-tab-sensitive-information.component';
+
+describe('DeltagareTabSensitiveInformationComponent', () => {
+ let component: DeltagareTabSensitiveInformationComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DeltagareTabSensitiveInformationComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DeltagareTabSensitiveInformationComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.ts
new file mode 100644
index 0000000..d48e3ef
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component.ts
@@ -0,0 +1,15 @@
+import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
+import { Avrop } from '@msfa-models/avrop.model';
+import { Disability } from '@msfa-models/disability.model';
+
+@Component({
+ selector: 'msfa-deltagare-tab-sensitive-information',
+ templateUrl: './deltagare-tab-sensitive-information.component.html',
+ styleUrls: ['./deltagare-tab-sensitive-information.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DeltagareTabSensitiveInformationComponent {
+ @Input() avropInformation: Avrop;
+ @Input() workLanguages: string[];
+ @Input() disabilities: Disability[];
+}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html
index a78f01b..3781135 100644
--- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.html
@@ -1,240 +1,70 @@
-
+
-
-
-
-
Personuppgifter
-
- - Namn:
- - {{ deltagare.fullName }}
- - Personnummer:
- -
-
-
-
- - {{address.type}}:
- -
-
- {{ address.street }}
- {{ address.postalCode }} {{ address.city }}
-
-
-
- - Telefon:
-
-
- - {{ phoneNumber.type }}: {{phoneNumber.number}}
-
-
- - E-postadress:
- -
- {{ deltagare.email }}
-
-
-
-
-
Om tjänsten
-
- - Tillhörande tjänst:
- - {{ deltagare.avropInformation.tjanst }}
- - Datum för tjänstens början:
- -
-
-
- - Datum för tjänstens slut:
- -
-
-
- - Deltagandefrekvens:
- -
- {{ deltagare.avropInformation.participationFrequency }}
-
- - Nivå:
- -
- {{ deltagare.avropInformation.trackName }}
-
- - Utförande verksamhet:
- -
- {{ deltagare.avropInformation.utforandeVerksamhet }}
-
- - Utförande adress:
- -
- {{ deltagare.avropInformation.utforandeAdress }}
-
- - Genomförandereferens:
- -
- {{ deltagare.avropInformation.genomforandeReferens }}
-
-
-
-
-
Handledare
-
- - Tilldelad handledare:
- -
- {{ deltagare.avropInformation.handledare }}
-
-
-
-
+
+
+
+
-
-
-
Skapa ny rapport
-
Här kan du skicka rapporter om deltagaren till arbetsförmedlingen.
-
-
-
-
-
-
-
Inskickade rapporter
-
-
-
-
-
-
-
Arbetslivserfarenhet
-
-
-
-
-
- {{ accordionExpanded ? 'Dölj' : 'Visa' }} fler arbetsgivare
-
-
-
-
-
-
Utbildning
-
- - Högsta utbildningsnivå:
- -
- {{ deltagare.highestEducation.level.description }}: {{ deltagare.highestEducation.sunKod.description
- }}
-
- Utbildningar:
-
-
-
-
-
Körkort
-
- - Har körkort
- - {{deltagare.driversLicense.licenses.length ? 'Ja' : 'Nej'}}
-
- - Körkortsklasser
- - {{deltagare.driversLicense.licenses.join(', ')}}
- - Tillgång till bil
- - {{deltagare.driversLicense.accessToCar ? 'Ja' : 'Nej'}}
-
-
-
-
-
-
-
-
-
Funktionsnedsättningar
-
-
- - Funktionsnedsättning {{index + 1}}
- -
- {{ disability.title }}
- {{ disability.description }}
-
-
-
-
-
-
Språk
-
- - Behov av tolk:
- -
- {{deltagare.avropInformation.tolkbehov ? 'Ja (' + deltagare.avropInformation.tolkbehov + ')' : 'Nej'}}
-
- - Behov av språkstöd:
- -
- {{deltagare.avropInformation.sprakstod ? 'Ja (' + deltagare.avropInformation.sprakstod + ')' : 'Nej'}}
-
- - Språk som kan användas på jobbet:
- - {{ deltagare.workLanguages.join(', ')}}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -242,15 +72,5 @@
-
-
-
-
- -
- Info saknas
-
-
-
- -
- Info saknas
+
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.scss b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.scss
index dddea31..e69cf87 100644
--- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.scss
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.scss
@@ -1,6 +1,4 @@
-@import 'mixins/list';
@import 'variables/gutters';
-@import 'variables/z-index';
.deltagare-card {
&__tab-contents {
@@ -9,68 +7,10 @@
margin: 0 $digi--layout--gutter--l;
}
- &__tab-column {
- flex-grow: 1;
- flex-shrink: 1;
- flex-basis: 0;
- }
-
- &__select-report {
- max-width: var(--digi--typography--text--max-width);
- }
-
- &__cta-wrapper {
- margin-top: $digi--layout--gutter--l;
- margin-bottom: $digi--layout--gutter--xl;
- }
-
- dd {
- margin: 0 0 1rem;
- }
-
- dt,
- &__subheading {
- font-size: var(--digi--typography--font-size--desktop);
- font-weight: var(--digi--typography--font-weight--semibold);
- margin: var(--digi--layout--gutter--s) 0 0;
-
- &--with-margin {
- font-size: var(--digi--typography--font-size--h3);
- margin-bottom: var(--digi--layout--gutter--s);
- }
- }
-
- &__experience-list {
- @include msfa__reset-list;
- }
-
- &__accordion {
- display: block;
- margin-top: var(--digi--layout--gutter);
- }
-
- &__accordion-trigger {
- font-weight: var(--digi--typography--font-weight--semibold);
- }
-
- &__popover {
- display: inline-block;
- margin-left: var(--digi--layout--gutter--s);
-
- ::ng-deep .digi-ng-popover__container {
- z-index: $msfa__z-index-popover;
- }
- }
-
- &__header,
- &__footer {
+ &__header {
display: flex;
flex-direction: row-reverse;
align-items: center;
justify-content: space-between;
}
-
- &__footer {
- margin-top: $digi--layout--gutter--l;
- }
}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.spec.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.spec.ts
index 8308801..e12814f 100644
--- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.spec.ts
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.spec.ts
@@ -1,7 +1,6 @@
-import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
-import { ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
import { DeltagareCardComponent } from './deltagare-card.component';
@@ -14,7 +13,8 @@ describe('DeltagareCardComponent', () => {
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [DeltagareCardComponent, LayoutComponent],
- imports: [RouterTestingModule, HttpClientTestingModule, DigiNgSkeletonBaseModule, ReactiveFormsModule],
+ imports: [RouterTestingModule, HttpClientTestingModule],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
})
);
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts
index b590b6a..b108100 100644
--- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.component.ts
@@ -1,14 +1,21 @@
-import { FormSelectItem } from '@af/digi-ng/_form/form-select';
import { ChangeDetectionStrategy, Component } from '@angular/core';
-import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
-import { ActivatedRoute, Router } from '@angular/router';
+import { ActivatedRoute } from '@angular/router';
+import { Feature } from '@msfa-enums/feature.enum';
import { IconType } from '@msfa-enums/icon-type.enum';
-import { Deltagare } from '@msfa-models/deltagare.model';
-import { ReportsData } from '@msfa-models/reports.model';
+import { RoleEnum } from '@msfa-enums/role.enum';
+import { environment } from '@msfa-environment';
+import { Avrop } from '@msfa-models/avrop.model';
+import { ContactInformation } from '@msfa-models/contact-information.model';
+import { Disability } from '@msfa-models/disability.model';
+import { DriversLicense } from '@msfa-models/drivers-license.model';
+import { Education } from '@msfa-models/education.model';
+import { HighestEducation } from '@msfa-models/highest-education.model';
+import { Role } from '@msfa-models/role.model';
import { WorkExperience } from '@msfa-models/work-experience.model';
-import { DeltagareService } from '@msfa-services/api/deltagare.service';
-import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { UserService } from '@msfa-services/api/user.service';
+import { DeltagareCardService } from '@msfa-services/deltagare-card.service';
+import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
+import { distinctUntilChanged, filter, map, shareReplay, startWith, switchMap } from 'rxjs/operators';
@Component({
selector: 'msfa-deltagare-card',
@@ -17,85 +24,110 @@ import { map } from 'rxjs/operators';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DeltagareCardComponent {
- deltagare$: Observable = this.deltagareService.deltagare$;
- reportsData$: Observable = this.deltagareService.reportsData$;
+ private _activeFeatures: Feature[] = environment.activeFeatures;
+ private _activeTab$ = new BehaviorSubject('0');
+ private _userRoles: Role[] = this.userService.userRolesSnapshot;
- readonly reportsFormControlName = 'reports';
-
- reportPickerFormGroup: FormGroup = this.formBuilder.group({
- // eslint-disable-next-line @typescript-eslint/unbound-method
- reports: this.formBuilder.control('', [Validators.required]),
- });
-
- selectableReportTypes: Array = [
- { name: 'Avvikelse', value: 'avvikelse' },
- { name: 'Gemensam Planering', value: 'planering' },
- ];
- selectedReportType: FormSelectItem;
-
- firstVisibleWorkExperiences$: Observable = this.deltagare$.pipe(
- map(deltagare => deltagare.workExperiences.slice(0, 2))
+ public activeTab$: Observable = this._activeTab$.asObservable();
+ public currentDeltagareId$: Observable = this.activatedRoute.params.pipe(
+ map(params => params.deltagareId as string),
+ distinctUntilChanged(([prevDeltagareId], [currDeltagareId]) => prevDeltagareId === currDeltagareId)
);
- hiddenWorkExperiences$: Observable = this.deltagare$.pipe(
- map(deltagare => deltagare.workExperiences.slice(2))
+ contactInformation$: Observable = combineLatest([this.currentDeltagareId$]).pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchContactInformation$(deltagareId)),
+ shareReplay(1)
+ );
+ avropInformation$: Observable = combineLatest([this.currentDeltagareId$, this._activeTab$]).pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchAvropInformation$(deltagareId)),
+ shareReplay(1)
+ );
+ workExperiences$: Observable = this.currentDeltagareId$.pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchWorkExperiences$(deltagareId)),
+ shareReplay(1)
+ );
+ highestEducation$: Observable = this.currentDeltagareId$.pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchHighestEducation$(deltagareId)),
+ shareReplay(1)
+ );
+ educations$: Observable = this.currentDeltagareId$.pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchEducations$(deltagareId)),
+ shareReplay(1)
+ );
+ driversLicense$: Observable = this.currentDeltagareId$.pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchDriversLicense$(deltagareId)),
+ shareReplay(1)
+ );
+ workLanguages$: Observable = this.currentDeltagareId$.pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchWorkLanguages$(deltagareId)),
+ shareReplay(1)
+ );
+ disabilities$: Observable = this.currentDeltagareId$.pipe(
+ switchMap(([deltagareId]) => this.deltagareCardService.fetchDisabilities$(deltagareId)),
+ shareReplay(1)
+ );
+
+ tab0Loading$: Observable = combineLatest([this.contactInformation$, this.avropInformation$]).pipe(
+ map(([contactInformation, avropInformation]) => !(contactInformation && avropInformation)),
+ startWith(true)
+ );
+
+ tab2Loading$: Observable = combineLatest([
+ this.workExperiences$,
+ this.highestEducation$,
+ this.educations$,
+ this.driversLicense$,
+ ]).pipe(
+ map(
+ ([workExperiences, highestEducation, educations, driversLicense]) =>
+ !(workExperiences && highestEducation && educations && driversLicense)
+ ),
+ startWith(true)
+ );
+ tab3Loading$: Observable = combineLatest([
+ this.disabilities$,
+ this.avropInformation$,
+ this.workLanguages$,
+ ]).pipe(
+ map(([disabilities, avropInformation, workLanguages]) => !(disabilities && avropInformation && workLanguages)),
+ startWith(true)
+ );
+
+ get deltagareTjanstVisible(): boolean {
+ return this._userRoles?.some(
+ role => role.type === RoleEnum.MSFA_ReportAndPlanning || role.type === RoleEnum.MSFA_ReceiveDeltagare
+ );
+ }
+ get reportingTabVisible(): boolean {
+ return (
+ this._activeFeatures.includes(Feature.REPORTING) &&
+ this._userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning)
+ );
+ }
+ get experiencesVisible(): boolean {
+ return this._userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning);
+ }
+ get sensitiveDataVisible(): boolean {
+ return this._userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning);
+ }
+
+ firstVisibleWorkExperiences$: Observable = this.workExperiences$.pipe(
+ filter(workExperiences => !!workExperiences),
+ map(workExperiences => workExperiences.slice(0, 2))
+ );
+ hiddenWorkExperiences$: Observable = this.workExperiences$.pipe(
+ filter(workExperiences => !!workExperiences),
+ map(workExperiences => workExperiences.slice(2))
);
iconType = IconType;
- accordionExpanded = false;
constructor(
private activatedRoute: ActivatedRoute,
- private deltagareService: DeltagareService,
- private formBuilder: FormBuilder,
- private router: Router
- ) {
- this.deltagareService.setCurrentDeltagareId(this.activatedRoute.snapshot.params.deltagareId);
- }
+ private deltagareCardService: DeltagareCardService,
+ private userService: UserService
+ ) {}
- get reportsFormControl(): AbstractControl | null {
- return this.reportPickerFormGroup?.get(this.reportsFormControlName);
- }
-
- toggleAccordionExpanded(): void {
- this.accordionExpanded = !this.accordionExpanded;
- }
-
- setNewPage(page: number): void {
- this.deltagareService.setPage(page);
- }
-
- onFormSubmitted(event: Event, reportType: string, deltagareId: string): void {
- event.preventDefault();
-
- switch (reportType) {
- case 'planering':
- if (this.reportsFormControl.valid) {
- this.router.navigate(['/deltagare/planering', deltagareId]).catch(error => {
- console.error(error);
- });
- }
- break;
- case 'avvikelse':
- if (this.reportsFormControl.valid) {
- this.router.navigate(['/deltagare/rapportera', deltagareId]).catch(error => {
- console.error(error);
- });
- }
- break;
- default:
- return;
- }
-
- this.reportsFormControl.markAsTouched();
-
- if (!this.selectableReportTypes || this.reportPickerFormGroup.invalid) {
- return;
- }
-
- const selectedReportType = this.selectableReportTypes.find(report => {
- return report.value === this.reportsFormControl.value;
- });
-
- this.deltagareService.setReportType(selectedReportType);
+ setActiveTab(tab: number): void {
+ this._activeTab$.next(tab.toString());
}
}
diff --git a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.module.ts b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.module.ts
index 1b8faf8..7b8056d 100644
--- a/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.module.ts
+++ b/apps/mina-sidor-fa/src/app/pages/deltagare/pages/deltagare-card/deltagare-card.module.ts
@@ -1,39 +1,45 @@
import { DigiNgFormSelectModule } from '@af/digi-ng/_form/form-select';
import { DigiNgLayoutExpansionPanelModule } from '@af/digi-ng/_layout/layout-expansion-panel';
import { DigiNgLinkButtonModule } from '@af/digi-ng/_link/link-button';
-import { DigiNgLinkInternalModule } from '@af/digi-ng/_link/link-internal';
import { DigiNgPopoverModule } from '@af/digi-ng/_popover/popover';
-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 { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
-import { IconModule } from '@msfa-shared/components/icon/icon.module';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
+import { LoaderModule } from '@msfa-shared/components/loader/loader.module';
+import { DeltagareTabExperiencesComponent } from './components/deltagare-tab-experiences/deltagare-tab-experiences.component';
+import { DeltagareTabPersonalInformationComponent } from './components/deltagare-tab-personal-information/deltagare-tab-personal-information.component';
+import { DeltagareTabReportsComponent } from './components/deltagare-tab-reports/deltagare-tab-reports.component';
+import { DeltagareTabSensitiveInformationComponent } from './components/deltagare-tab-sensitive-information/deltagare-tab-sensitive-information.component';
import { ReportsModule } from './components/reports/reports.module';
import { DeltagareCardComponent } from './deltagare-card.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
- declarations: [DeltagareCardComponent],
+ declarations: [
+ DeltagareCardComponent,
+ DeltagareTabExperiencesComponent,
+ DeltagareTabReportsComponent,
+ DeltagareTabPersonalInformationComponent,
+ DeltagareTabSensitiveInformationComponent,
+ ],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: DeltagareCardComponent }]),
+ ReactiveFormsModule,
ReportsModule,
LayoutModule,
- DigiNgLinkInternalModule,
- IconModule,
BackLinkModule,
- DigiNgLayoutExpansionPanelModule,
HideTextModule,
- DigiNgSkeletonBaseModule,
+ LoaderModule,
+ DigiNgLayoutExpansionPanelModule,
DigiNgPopoverModule,
DigiNgLinkButtonModule,
DigiNgFormSelectModule,
- ReactiveFormsModule
],
exports: [DeltagareCardComponent],
})
-export class DeltagareCardModule { }
+export class DeltagareCardModule {}
diff --git a/apps/mina-sidor-fa/src/app/shared/components/layout/components/sidebar/sidebar.component.ts b/apps/mina-sidor-fa/src/app/shared/components/layout/components/sidebar/sidebar.component.ts
index 8ead4b8..73ba2f5 100644
--- a/apps/mina-sidor-fa/src/app/shared/components/layout/components/sidebar/sidebar.component.ts
+++ b/apps/mina-sidor-fa/src/app/shared/components/layout/components/sidebar/sidebar.component.ts
@@ -31,7 +31,9 @@ export class SidebarComponent {
get deltagareVisible(): boolean {
return (
this.activeFeatures.includes(Feature.DELTAGARE) &&
- this.userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning)
+ this.userRoles?.some(
+ role => role.type === RoleEnum.MSFA_ReportAndPlanning || role.type === RoleEnum.MSFA_ReceiveDeltagare
+ )
);
}
}
diff --git a/apps/mina-sidor-fa/src/app/shared/components/layout/layout.component.ts b/apps/mina-sidor-fa/src/app/shared/components/layout/layout.component.ts
index be37919..44c3784 100644
--- a/apps/mina-sidor-fa/src/app/shared/components/layout/layout.component.ts
+++ b/apps/mina-sidor-fa/src/app/shared/components/layout/layout.component.ts
@@ -1,6 +1,5 @@
import { NavigationBreadcrumbsItem } from '@af/digi-ng/_navigation/navigation-breadcrumbs';
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
-import { Title } from '@angular/platform-browser';
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
import { Employee } from '@msfa-models/employee.model';
diff --git a/apps/mina-sidor-fa/src/app/shared/components/loader/loader.component.scss b/apps/mina-sidor-fa/src/app/shared/components/loader/loader.component.scss
index 85cdb76..70aa16a 100644
--- a/apps/mina-sidor-fa/src/app/shared/components/loader/loader.component.scss
+++ b/apps/mina-sidor-fa/src/app/shared/components/loader/loader.component.scss
@@ -1,4 +1,5 @@
@import 'mixins/backdrop';
+@import 'variables/gutters';
@import 'variables/z-index';
@keyframes spinning {
@@ -12,6 +13,10 @@
align-items: center;
justify-content: center;
+ &--padded {
+ padding: $digi--layout--gutter--l;
+ }
+
&--absolute {
@include msfa__backdrop($msfa__z-index-backdrop, false);
}
diff --git a/apps/mina-sidor-fa/src/app/shared/enums/feature.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/feature.enum.ts
index c39c64a..0626aa2 100644
--- a/apps/mina-sidor-fa/src/app/shared/enums/feature.enum.ts
+++ b/apps/mina-sidor-fa/src/app/shared/enums/feature.enum.ts
@@ -8,4 +8,5 @@ export enum Feature {
MOCK_LOGIN,
VERSION_INFO,
ACCESSIBILITY_REPORT,
+ REPORTING,
}
diff --git a/apps/mina-sidor-fa/src/app/shared/enums/loader-type.enum.ts b/apps/mina-sidor-fa/src/app/shared/enums/loader-type.enum.ts
index bcc91e2..23fb4a9 100644
--- a/apps/mina-sidor-fa/src/app/shared/enums/loader-type.enum.ts
+++ b/apps/mina-sidor-fa/src/app/shared/enums/loader-type.enum.ts
@@ -1,4 +1,5 @@
export enum LoaderType {
FULL_SCREEN = 'fullscreen',
ABSOLUTE = 'absolute',
+ PADDED = 'padded',
}
diff --git a/apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts b/apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
index c49f554..cd69dc3 100644
--- a/apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
+++ b/apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
@@ -12,12 +12,12 @@ export class RoleGuard implements CanActivate {
constructor(private router: Router, private userService: UserService) {}
canActivate(route: ActivatedRouteSnapshot): Observable {
- const expectedRole: RoleEnum = route.data.expectedRole as RoleEnum;
+ const expectedRoles: RoleEnum[] = route.data.expectedRoles as RoleEnum[];
return this.userService.userRoles$.pipe(
filter(roles => !!roles),
map(roles => {
- const userHasRole = roles.some(role => role.type === expectedRole);
+ const userHasRole = roles.some(role => expectedRoles.includes(role.type));
if (userHasRole) {
return true;
diff --git a/apps/mina-sidor-fa/src/app/shared/models/api/avrop.response.model.ts b/apps/mina-sidor-fa/src/app/shared/models/api/avrop.response.model.ts
index 73eeeea..1672bd4 100644
--- a/apps/mina-sidor-fa/src/app/shared/models/api/avrop.response.model.ts
+++ b/apps/mina-sidor-fa/src/app/shared/models/api/avrop.response.model.ts
@@ -27,7 +27,8 @@ export interface AvropResponse {
sprakstod: string;
sparkod: string;
sparNamn: string;
- supervisorId: number;
+ handledareCiamUserId: string;
+ handledare: string;
recievedTimestamp: string;
}
diff --git a/apps/mina-sidor-fa/src/app/shared/models/avrop.model.ts b/apps/mina-sidor-fa/src/app/shared/models/avrop.model.ts
index a337838..0e018c1 100644
--- a/apps/mina-sidor-fa/src/app/shared/models/avrop.model.ts
+++ b/apps/mina-sidor-fa/src/app/shared/models/avrop.model.ts
@@ -19,6 +19,8 @@ export interface Avrop extends AvropCompact {
genomforandeReferens: number; // genomforandeReferens
participationFrequency: number; // deltagandeGrad
utforandeVerksamhet: string; // utforandeverksamhet
+ handledareCiamUserId: string;
+ handledare: string;
}
export interface AvropCompactData {
@@ -42,6 +44,8 @@ export function mapAvropResponseToAvrop(data: AvropResponse): Avrop {
genomforandeReferens,
deltagandeGrad,
utforandeverksamhet,
+ handledareCiamUserId,
+ handledare,
} = data;
return {
@@ -59,5 +63,7 @@ export function mapAvropResponseToAvrop(data: AvropResponse): Avrop {
genomforandeReferens,
participationFrequency: deltagandeGrad,
utforandeVerksamhet: utforandeverksamhet,
+ handledareCiamUserId,
+ handledare,
};
}
diff --git a/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts b/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts
index 3e1d6d1..5784e51 100644
--- a/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts
+++ b/apps/mina-sidor-fa/src/app/shared/models/error/custom-error.ts
@@ -52,7 +52,7 @@ export class CustomError implements Error {
}
}
-export function errorToCustomError(error: Error & { ngDebugContext: unknown }): CustomError {
+export function errorToCustomError(error: Error & { ngDebugContext?: unknown }): CustomError {
const type = CustomError.getErrorType(error);
const message = error.message || error;
const severity = ErrorSeverity.HIGH;
diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-api.service.ts
deleted file mode 100644
index 4c15092..0000000
--- a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare-api.service.ts
+++ /dev/null
@@ -1,267 +0,0 @@
-import { HttpClient } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
-import { SortOrder } from '@msfa-enums/sort-order.enum';
-import { environment } from '@msfa-environment';
-import { AvropResponse } from '@msfa-models/api/avrop.response.model';
-import { ContactInformationResponse } from '@msfa-models/api/contact-information.response.model';
-import { DeltagareCompactApiResponse } from '@msfa-models/api/deltagare.response.model';
-import { DisabilityResponse } from '@msfa-models/api/disability.response.model';
-import { DriversLicenseResponse } from '@msfa-models/api/drivers-license.response.model';
-import { EducationsResponse } from '@msfa-models/api/educations.response.model';
-import { HighestEducationResponse } from '@msfa-models/api/highest-education.response.model';
-import { Params } from '@msfa-models/api/params.model';
-import { TranslatorResponse } from '@msfa-models/api/translator.response.model';
-import { WorkExperiencesResponse } from '@msfa-models/api/work-experiences.response.model';
-import { WorkLanguagesResponse } from '@msfa-models/api/work-languages.response.model';
-import { Avrop, mapAvropResponseToAvrop } from '@msfa-models/avrop.model';
-import { ContactInformation, mapResponseToContactInformation } from '@msfa-models/contact-information.model';
-import {
- Deltagare,
- DeltagareCompact,
- DeltagareCompactData,
- mapResponseToDeltagareCompact
-} from '@msfa-models/deltagare.model';
-import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
-import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
-import { Education, mapResponseToEducation } from '@msfa-models/education.model';
-import { errorToCustomError } from '@msfa-models/error/custom-error';
-import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
-import { Sort } from '@msfa-models/sort.model';
-import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
-import { ErrorService } from '@msfa-services/error.service';
-import { sortFromToDates } from '@msfa-utils/sort.util';
-import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
-import { catchError, filter, map, switchMap } from 'rxjs/operators';
-
-@Injectable({
- providedIn: 'root',
-})
-export class DeltagareApiService extends UnsubscribeDirective {
- private _apiBaseUrl = `${environment.api.url}/deltagare`;
- private _currentDeltagareId$ = new BehaviorSubject(null);
- private _limit$ = new BehaviorSubject(20);
- private _page$ = new BehaviorSubject(1);
- private _sort$ = new BehaviorSubject>({ key: 'fullName', order: SortOrder.ASC });
- public sort$: Observable> = this._sort$.asObservable();
-
- constructor(private httpClient: HttpClient, private errorService: ErrorService) {
- super();
- super.unsubscribeOnDestroy(
- this._currentDeltagareId$
- .pipe(
- filter(currentDeltagareId => !!currentDeltagareId),
- switchMap(currentDeltagareId => this._fetchDeltagare$(currentDeltagareId))
- )
- .subscribe(deltagare => {
- this._deltagare$.next(deltagare);
- })
- );
- }
-
- private _deltagare$ = new BehaviorSubject(null);
- public deltagare$: Observable = this._deltagare$.asObservable();
-
- public allDeltagareData$: Observable = combineLatest([
- this._limit$,
- this._page$,
- this._sort$,
- ]).pipe(switchMap(([limit, page, sort]) => this._fetchAllDeltagare$(limit, page, sort)));
-
- public setSort(newSortKey: keyof DeltagareCompact): void {
- const currentSort = this._sort$.getValue();
- const order =
- currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
-
- this._sort$.next({ key: newSortKey, order });
- }
-
- public setPage(page: number): void {
- this._page$.next(page);
- }
-
- private _fetchAllDeltagare$(
- limit: number,
- page: number,
- sort: Sort
- ): Observable {
- const params: Params = {
- sort: sort.key as string,
- order: sort.order as string,
- limit: limit.toString(),
- page: page.toString(),
- };
-
- return this.httpClient
- .get(this._apiBaseUrl, {
- params,
- })
- .pipe(
- map(({ data, meta }) => {
- return { data: data.map(deltagare => mapResponseToDeltagareCompact(deltagare)), meta };
- })
- );
- }
-
- public setCurrentDeltagareId(currentDeltagareId: string): void {
- this._deltagare$.next(null);
- this._currentDeltagareId$.next(currentDeltagareId);
- }
-
- private _fetchContactInformation$(id: string): Observable> {
- return this.httpClient.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${id}/contact`).pipe(
- map(({ data }) => mapResponseToContactInformation(data)),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- private _fetchDriversLicense$(id: string): Observable> {
- return this.httpClient.get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${id}/driverlicense`).pipe(
- map(({ data }) => mapResponseToDriversLicense(data)),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- private _fetchHighestEducation$(id: string): Observable> {
- return this.httpClient
- .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${id}/educationlevels/highest`)
- .pipe(
- map(({ data }) => mapResponseToHighestEducation(data)),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- private _fetchEducations$(id: string): Observable {
- return this.httpClient.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${id}/educations`).pipe(
- map(({ data }) =>
- data.utbildningar
- ? data.utbildningar.sort((a, b) =>
- sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
- )
- : []
- ),
- map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchTranslator$(id: string): Observable {
- return this.httpClient.get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${id}/translator`).pipe(
- map(({ data }) => data.sprak?.beskrivning || null),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of('');
- })
- );
- }
-
- private _fetchWorkLanguages$(id: string): Observable {
- return this.httpClient.get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${id}/work/languages`).pipe(
- map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchDisabilities$(id: string): Observable {
- return this.httpClient.get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${id}/work/disabilities`).pipe(
- map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchWorkExperiences$(id: string): Observable {
- return this.httpClient.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${id}/work/experiences`).pipe(
- map(
- ({ data }) =>
- data?.arbetslivserfarenheter?.sort((a, b) =>
- sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
- ) || []
- ),
- map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchAvropInformation$(id: string): Observable> {
- return this.httpClient.get<{ data: AvropResponse }>(`${this._apiBaseUrl}/${id}/avrop`).pipe(
- map(({ data }) => (data ? mapAvropResponseToAvrop(data) : {})),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- // As TypeScript has some limitations regarding combining Observables this way,
- // we need to type it manually when exceeding 6 Observables inside a combineLatest.
- // Read: https://github.com/ReactiveX/rxjs/issues/3601#issuecomment-384711601
- private _fetchDeltagare$(id: string): Observable {
- return combineLatest([
- this._fetchContactInformation$(id),
- this._fetchDriversLicense$(id),
- this._fetchHighestEducation$(id),
- this._fetchEducations$(id),
- this._fetchTranslator$(id),
- this._fetchWorkLanguages$(id),
- this._fetchDisabilities$(id),
- this._fetchWorkExperiences$(id),
- this._fetchAvropInformation$(id),
- ]).pipe(
- map(
- ([
- contactInformation,
- driversLicense,
- highestEducation,
- educations,
- translator,
- workLanguages,
- disabilities,
- workExperiences,
- avropInformation,
- ]: [
- ContactInformation,
- DriversLicense,
- HighestEducation,
- Education[],
- string,
- string[],
- Disability[],
- WorkExperience[],
- Avrop
- ]) => ({
- id,
- ...contactInformation,
- driversLicense,
- highestEducation,
- educations,
- translator,
- workLanguages,
- disabilities,
- workExperiences,
- avropInformation,
- })
- )
- );
- }
-}
diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts
new file mode 100644
index 0000000..eb3e4e6
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.api.service.ts
@@ -0,0 +1,210 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { environment } from '@msfa-environment';
+import { AvropResponse } from '@msfa-models/api/avrop.response.model';
+import { ContactInformationResponse } from '@msfa-models/api/contact-information.response.model';
+import { DeltagareCompactApiResponse } from '@msfa-models/api/deltagare.response.model';
+import { DisabilityResponse } from '@msfa-models/api/disability.response.model';
+import { DriversLicenseResponse } from '@msfa-models/api/drivers-license.response.model';
+import { EducationsResponse } from '@msfa-models/api/educations.response.model';
+import { HighestEducationResponse } from '@msfa-models/api/highest-education.response.model';
+import { Params } from '@msfa-models/api/params.model';
+import { TranslatorResponse } from '@msfa-models/api/translator.response.model';
+import { WorkExperiencesResponse } from '@msfa-models/api/work-experiences.response.model';
+import { WorkLanguagesResponse } from '@msfa-models/api/work-languages.response.model';
+import { Avrop, mapAvropResponseToAvrop } from '@msfa-models/avrop.model';
+import { ContactInformation, mapResponseToContactInformation } from '@msfa-models/contact-information.model';
+import { DeltagareCompact, DeltagareCompactData, mapResponseToDeltagareCompact } from '@msfa-models/deltagare.model';
+import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
+import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
+import { Education, mapResponseToEducation } from '@msfa-models/education.model';
+import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
+import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
+import { ReportsData } from '@msfa-models/reports.model';
+import { Sort } from '@msfa-models/sort.model';
+import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
+import { sortFromToDates } from '@msfa-utils/sort.util';
+import { Observable, of } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class DeltagareApiService {
+ private _apiBaseUrl = `${environment.api.url}/deltagare`;
+
+ constructor(private httpClient: HttpClient) {}
+
+ public fetchAllDeltagare$(
+ limit: number,
+ page: number,
+ sort: Sort,
+ onlyMyDeltagare?: boolean
+ ): Observable {
+ const params: Params = {
+ sort: sort.key as string,
+ order: sort.order as string,
+ limit: limit.toString(),
+ page: page.toString(),
+ };
+
+ if (onlyMyDeltagare) {
+ params.onlyMyDeltagare = onlyMyDeltagare.toString();
+ }
+ return this.httpClient
+ .get(this._apiBaseUrl, {
+ params,
+ })
+ .pipe(
+ map(({ data, meta }) => {
+ return { data: data.map(deltagare => mapResponseToDeltagareCompact(deltagare)), meta };
+ }),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta deltagare.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchReports$(limit: number, page: number, deltagareId: string): Observable {
+ return of({ data: [], meta: null });
+
+ // TODO: When the API/Mock-API has implemented the endpoint, we can remove use following code
+ // to make API-requests.
+
+ // const params: { [param: string]: string | string[] } = {
+ // id: deltagareId.toString(),
+ // limit: limit.toString(),
+ // page: page.toString(),
+ // };
+
+ // return this.httpClient
+ // .get(`${this._apiBaseUrl}/report`, {
+ // params,
+ // })
+ // .pipe(
+ // map(({ data, meta }) => {
+ // data.sort((reportA, reportB) => (+reportA.sendDate < +reportB.sendDate ? 1 : -1));
+ // return { data: data.map(report => mapReportsResponseToReport(report)), meta };
+ // })
+ // );
+ }
+
+ public fetchContactInformation$(id: string): Observable {
+ return this.httpClient.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${id}/contact`).pipe(
+ map(({ data }) => mapResponseToContactInformation(data)),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta kontaktinformation.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchDriversLicense$(id: string): Observable {
+ return this.httpClient.get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${id}/driverlicense`).pipe(
+ map(({ data }) => mapResponseToDriversLicense(data)),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta körkortsinformation.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchHighestEducation$(id: string): Observable {
+ return this.httpClient
+ .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${id}/educationlevels/highest`)
+ .pipe(
+ map(({ data }) => mapResponseToHighestEducation(data)),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta högsta utbildning.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchEducations$(id: string): Observable {
+ return this.httpClient.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${id}/educations`).pipe(
+ map(({ data }) =>
+ data.utbildningar
+ ? data.utbildningar.sort((a, b) =>
+ sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
+ )
+ : []
+ ),
+ map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta utbildningar.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchTranslator$(id: string): Observable {
+ return this.httpClient.get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${id}/translator`).pipe(
+ map(({ data }) => data.sprak?.beskrivning || null),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta tolkinformation.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchWorkLanguages$(id: string): Observable {
+ return this.httpClient.get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${id}/work/languages`).pipe(
+ map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({
+ ...error,
+ message: `Kunde inte hämta språk som kan användas på jobbet.\n\n${error.message}`,
+ })
+ );
+ })
+ );
+ }
+
+ public fetchDisabilities$(id: string): Observable {
+ return this.httpClient.get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${id}/work/disabilities`).pipe(
+ map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta funktionsnedsättningar.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchWorkExperiences$(id: string): Observable {
+ return this.httpClient.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${id}/work/experiences`).pipe(
+ map(
+ ({ data }) =>
+ data?.arbetslivserfarenheter?.sort((a, b) =>
+ sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
+ ) || []
+ ),
+ map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta arbetslivserfarenheter.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+
+ public fetchAvropInformation$(id: string): Observable> {
+ return this.httpClient.get<{ data: AvropResponse }>(`${this._apiBaseUrl}/${id}/avrop`).pipe(
+ map(({ data }) => (data ? mapAvropResponseToAvrop(data) : {})),
+ catchError((error: Error) => {
+ throw new CustomError(
+ errorToCustomError({ ...error, message: `Kunde inte hämta avropsinformation.\n\n${error.message}` })
+ );
+ })
+ );
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.service.ts
deleted file mode 100644
index ee971cf..0000000
--- a/apps/mina-sidor-fa/src/app/shared/services/api/deltagare.service.ts
+++ /dev/null
@@ -1,316 +0,0 @@
-import { FormSelectItem } from '@af/digi-ng/_form/form-select';
-import { HttpClient } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
-import { SortOrder } from '@msfa-enums/sort-order.enum';
-import { environment } from '@msfa-environment';
-import { AvropResponse } from '@msfa-models/api/avrop.response.model';
-import { ContactInformationResponse } from '@msfa-models/api/contact-information.response.model';
-import { DeltagareCompactApiResponse } from '@msfa-models/api/deltagare.response.model';
-import { DisabilityResponse } from '@msfa-models/api/disability.response.model';
-import { DriversLicenseResponse } from '@msfa-models/api/drivers-license.response.model';
-import { EducationsResponse } from '@msfa-models/api/educations.response.model';
-import { HighestEducationResponse } from '@msfa-models/api/highest-education.response.model';
-import { ReportResponse } from '@msfa-models/api/report.response.model';
-import { Params } from '@msfa-models/api/params.model';
-import { TranslatorResponse } from '@msfa-models/api/translator.response.model';
-import { WorkExperiencesResponse } from '@msfa-models/api/work-experiences.response.model';
-import { WorkLanguagesResponse } from '@msfa-models/api/work-languages.response.model';
-import { Avrop, mapAvropResponseToAvrop } from '@msfa-models/avrop.model';
-import { ContactInformation, mapResponseToContactInformation } from '@msfa-models/contact-information.model';
-import {
- Deltagare,
- DeltagareCompact,
- DeltagareCompactData,
- mapResponseToDeltagareCompact
-} from '@msfa-models/deltagare.model';
-import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
-import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
-import { Education, mapResponseToEducation } from '@msfa-models/education.model';
-import { errorToCustomError } from '@msfa-models/error/custom-error';
-import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
-import { mapReportsResponseToReport, ReportsData } from '@msfa-models/reports.model';
-import { Sort } from '@msfa-models/sort.model';
-import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
-import { ErrorService } from '@msfa-services/error.service';
-import { sortFromToDates } from '@msfa-utils/sort.util';
-import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs';
-import { catchError, filter, map, switchMap } from 'rxjs/operators';
-
-@Injectable({
- providedIn: 'root',
-})
-export class DeltagareService extends UnsubscribeDirective {
- private _apiBaseUrl = `${environment.api.url}/deltagare`;
- private _currentDeltagareId$ = new BehaviorSubject(null);
- private _limit$ = new BehaviorSubject(20);
- private _page$ = new BehaviorSubject(1);
- private _sort$ = new BehaviorSubject>({ key: 'fullName', order: SortOrder.ASC });
- public sort$: Observable> = this._sort$.asObservable();
- private _reportType$ = new BehaviorSubject(null);
- reportType$: Observable = this._reportType$.asObservable();
- private _onlyMyDeltagare$ = new BehaviorSubject(false);
- public onlyMyDeltagare$: Observable = this._onlyMyDeltagare$.asObservable();
-
- constructor(private httpClient: HttpClient, private errorService: ErrorService) {
- super();
- super.unsubscribeOnDestroy(
- this._currentDeltagareId$
- .pipe(
- filter(currentDeltagareId => !!currentDeltagareId),
- switchMap(currentDeltagareId => this._fetchDeltagare$(currentDeltagareId))
- )
- .subscribe(deltagare => {
- this._deltagare$.next(deltagare);
- })
- );
- }
-
- private _deltagare$ = new BehaviorSubject(null);
- public deltagare$: Observable = this._deltagare$.asObservable();
-
- public allDeltagareData$: Observable = combineLatest([
- this._limit$,
- this._page$,
- this._sort$,
- this._onlyMyDeltagare$,
- ]).pipe(
- switchMap(([limit, page, sort, onlyMyDeltagare]) => this._fetchAllDeltagare$(limit, page, sort, onlyMyDeltagare))
- );
-
- public reportsData$: Observable = combineLatest([
- this._limit$,
- this._page$
- ]).pipe(switchMap(([limit, page]) => this._fetchReports$(limit, page)));
-
- public setSort(newSortKey: keyof DeltagareCompact): void {
- const currentSort = this._sort$.getValue();
- const order =
- currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
-
- this._sort$.next({ key: newSortKey, order });
- }
-
- public setPage(page: number): void {
- this._page$.next(page);
- }
-
- private _fetchAllDeltagare$(
- limit: number,
- page: number,
- sort: Sort,
- onlyMyDeltagare?: boolean
- ): Observable {
- const params: Params = {
- sort: sort.key as string,
- order: sort.order as string,
- limit: limit.toString(),
- page: page.toString(),
- };
-
- if (onlyMyDeltagare) {
- params.onlyMyDeltagare = onlyMyDeltagare.toString();
- }
- return this.httpClient
- .get(this._apiBaseUrl, {
- params,
- })
- .pipe(
- map(({ data, meta }) => {
- return { data: data.map(deltagare => mapResponseToDeltagareCompact(deltagare)), meta };
- })
- );
- }
-
- private _fetchReports$(
- limit: number,
- page: number
- ): Observable {
- const params: { [param: string]: string | string[] } = {
- limit: limit.toString(),
- page: page.toString()
- };
-
- return this.httpClient
- .get(`${this._apiBaseUrl}/report`, {
- params
- })
- .pipe(
- map(({ data, meta }) => {
- data.sort((reportA, reportB) =>
- +reportA.sendDate < +reportB.sendDate ? 1 : -1)
- return { data: data.map(report => mapReportsResponseToReport(report)), meta };
- })
- );
- }
-
- public setReportType(reportType: FormSelectItem): void {
- this._reportType$.next(reportType);
- }
-
- public setCurrentDeltagareId(currentDeltagareId: string): void {
- this._deltagare$.next(null);
- this._currentDeltagareId$.next(currentDeltagareId);
- }
-
- public setOnlyMyDeltagare(value: boolean): void {
- this._onlyMyDeltagare$.next(value);
- }
-
- private _fetchContactInformation$(id: string): Observable> {
- return this.httpClient.get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${id}/contact`).pipe(
- map(({ data }) => mapResponseToContactInformation(data)),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- private _fetchDriversLicense$(id: string): Observable> {
- return this.httpClient.get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${id}/driverlicense`).pipe(
- map(({ data }) => mapResponseToDriversLicense(data)),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- private _fetchHighestEducation$(id: string): Observable> {
- return this.httpClient
- .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${id}/educationlevels/highest`)
- .pipe(
- map(({ data }) => mapResponseToHighestEducation(data)),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- private _fetchEducations$(id: string): Observable {
- return this.httpClient.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${id}/educations`).pipe(
- map(({ data }) =>
- data.utbildningar
- ? data.utbildningar.sort((a, b) =>
- sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
- )
- : []
- ),
- map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchTranslator$(id: string): Observable {
- return this.httpClient.get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${id}/translator`).pipe(
- map(({ data }) => data.sprak?.beskrivning || null),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of('');
- })
- );
- }
-
- private _fetchWorkLanguages$(id: string): Observable {
- return this.httpClient.get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${id}/work/languages`).pipe(
- map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchDisabilities$(id: string): Observable {
- return this.httpClient.get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${id}/work/disabilities`).pipe(
- map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchWorkExperiences$(id: string): Observable {
- return this.httpClient.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${id}/work/experiences`).pipe(
- map(
- ({ data }) =>
- data?.arbetslivserfarenheter?.sort((a, b) =>
- sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
- ) || []
- ),
- map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of([]);
- })
- );
- }
-
- private _fetchAvropInformation$(id: string): Observable> {
- return this.httpClient.get<{ data: AvropResponse }>(`${this._apiBaseUrl}/${id}/avrop`).pipe(
- map(({ data }) => (data ? mapAvropResponseToAvrop(data) : {})),
- catchError(error => {
- this.errorService.add(errorToCustomError(error));
- return of({});
- })
- );
- }
-
- // As TypeScript has some limitations regarding combining Observables this way,
- // we need to type it manually when exceeding 6 Observables inside a combineLatest.
- // Read: https://github.com/ReactiveX/rxjs/issues/3601#issuecomment-384711601
- private _fetchDeltagare$(id: string): Observable {
- return combineLatest([
- this._fetchContactInformation$(id),
- this._fetchDriversLicense$(id),
- this._fetchHighestEducation$(id),
- this._fetchEducations$(id),
- this._fetchTranslator$(id),
- this._fetchWorkLanguages$(id),
- this._fetchDisabilities$(id),
- this._fetchWorkExperiences$(id),
- this._fetchAvropInformation$(id),
- ]).pipe(
- map(
- ([
- contactInformation,
- driversLicense,
- highestEducation,
- educations,
- translator,
- workLanguages,
- disabilities,
- workExperiences,
- avropInformation,
- ]: [
- ContactInformation,
- DriversLicense,
- HighestEducation,
- Education[],
- string,
- string[],
- Disability[],
- WorkExperience[],
- Avrop
- ]) => ({
- id,
- ...contactInformation,
- driversLicense,
- highestEducation,
- educations,
- translator,
- workLanguages,
- disabilities,
- workExperiences,
- avropInformation,
- })
- )
- );
- }
-}
diff --git a/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts b/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts
index 0f0cc54..e3813fb 100644
--- a/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts
+++ b/apps/mina-sidor-fa/src/app/shared/services/api/user.service.ts
@@ -34,6 +34,10 @@ export class UserService extends UnsubscribeDirective {
public userRoles$: Observable = this._userRoles$.asObservable();
private _selectedOrganizationNumber$ = new BehaviorSubject(null);
+ public get userRolesSnapshot(): Role[] {
+ return this._userRoles$.getValue();
+ }
+
constructor(private httpClient: HttpClient, private authenticationService: AuthenticationService) {
super();
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
diff --git a/apps/mina-sidor-fa/src/app/shared/services/deltagare-card.service.ts b/apps/mina-sidor-fa/src/app/shared/services/deltagare-card.service.ts
new file mode 100644
index 0000000..50e865c
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/shared/services/deltagare-card.service.ts
@@ -0,0 +1,46 @@
+import { Injectable } from '@angular/core';
+import { Avrop } from '@msfa-models/avrop.model';
+import { ContactInformation } from '@msfa-models/contact-information.model';
+import { Disability } from '@msfa-models/disability.model';
+import { DriversLicense } from '@msfa-models/drivers-license.model';
+import { Education } from '@msfa-models/education.model';
+import { HighestEducation } from '@msfa-models/highest-education.model';
+import { ReportsData } from '@msfa-models/reports.model';
+import { WorkExperience } from '@msfa-models/work-experience.model';
+import { Observable } from 'rxjs';
+import { DeltagareApiService } from './api/deltagare.api.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class DeltagareCardService {
+ constructor(private deltagareApiService: DeltagareApiService) {}
+
+ public fetchContactInformation$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchContactInformation$(deltagareId);
+ }
+ public fetchAvropInformation$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchAvropInformation$(deltagareId) as Observable;
+ }
+ public fetchWorkExperiences$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchWorkExperiences$(deltagareId);
+ }
+ public fetchHighestEducation$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchHighestEducation$(deltagareId) as Observable;
+ }
+ public fetchEducations$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchEducations$(deltagareId);
+ }
+ public fetchDriversLicense$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchDriversLicense$(deltagareId) as Observable;
+ }
+ public fetchWorkLanguages$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchWorkLanguages$(deltagareId);
+ }
+ public fetchDisabilities$(deltagareId: string): Observable {
+ return this.deltagareApiService.fetchDisabilities$(deltagareId);
+ }
+ public fetchReports$(limit: number, page: number, deltagareId: string): Observable {
+ return this.deltagareApiService.fetchReports$(limit, page, deltagareId);
+ }
+}
diff --git a/apps/mina-sidor-fa/src/app/shared/services/deltagare.service.ts b/apps/mina-sidor-fa/src/app/shared/services/deltagare.service.ts
new file mode 100644
index 0000000..284fa83
--- /dev/null
+++ b/apps/mina-sidor-fa/src/app/shared/services/deltagare.service.ts
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+import { SortOrder } from '@msfa-enums/sort-order.enum';
+import { DeltagareCompact, DeltagareCompactData } from '@msfa-models/deltagare.model';
+import { Sort } from '@msfa-models/sort.model';
+import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+import { DeltagareApiService } from './api/deltagare.api.service';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class DeltagareService {
+ private _limit$ = new BehaviorSubject(20);
+ private _page$ = new BehaviorSubject(1);
+ private _sort$ = new BehaviorSubject>({ key: 'fullName', order: SortOrder.ASC });
+ public sort$: Observable> = this._sort$.asObservable();
+ private _onlyMyDeltagare$ = new BehaviorSubject(false);
+ public onlyMyDeltagare$: Observable = this._onlyMyDeltagare$.asObservable();
+ public allDeltagareData$: Observable = combineLatest([
+ this._limit$,
+ this._page$,
+ this._sort$,
+ this._onlyMyDeltagare$,
+ ]).pipe(
+ switchMap(([limit, page, sort, onlyMyDeltagare]) =>
+ this.deltagareApiService.fetchAllDeltagare$(limit, page, sort, onlyMyDeltagare)
+ )
+ );
+
+ constructor(private deltagareApiService: DeltagareApiService) {}
+
+ public setSort(newSortKey: keyof DeltagareCompact): void {
+ const currentSort = this._sort$.getValue();
+ const order =
+ currentSort.key === newSortKey && currentSort.order === SortOrder.ASC ? SortOrder.DESC : SortOrder.ASC;
+
+ this._sort$.next({ key: newSortKey, order });
+ }
+
+ public setPage(page: number): void {
+ this._page$.next(page);
+ }
+
+ public setOnlyMyDeltagare(value: boolean): void {
+ this._onlyMyDeltagare$.next(value);
+ }
+}
diff --git a/apps/mina-sidor-fa/src/environments/active-features.ts b/apps/mina-sidor-fa/src/environments/active-features.ts
index 558c712..00aae06 100644
--- a/apps/mina-sidor-fa/src/environments/active-features.ts
+++ b/apps/mina-sidor-fa/src/environments/active-features.ts
@@ -5,6 +5,8 @@ export const ACTIVE_FEATURES_PROD: Feature[] = [
Feature.MY_ACCOUNT,
Feature.MY_ORGANIZATION,
Feature.ACCESSIBILITY_REPORT,
+ Feature.DELTAGARE,
+ Feature.AVROP,
];
export const ACTIVE_FEATURES_TEST: Feature[] = [
@@ -16,4 +18,5 @@ export const ACTIVE_FEATURES_TEST: Feature[] = [
Feature.RELEASES,
Feature.VERSION_INFO,
Feature.ACCESSIBILITY_REPORT,
+ Feature.REPORTING,
];
diff --git a/mock-api/mina-sidor-fa/scripts/avrop.js b/mock-api/mina-sidor-fa/scripts/avrop.js
index 862523e..d0c0c21 100644
--- a/mock-api/mina-sidor-fa/scripts/avrop.js
+++ b/mock-api/mina-sidor-fa/scripts/avrop.js
@@ -57,6 +57,7 @@ function generateAvrop(amount = 10, deltagare, handledare) {
sparkod: track.kod,
sparNamn: track.name,
handledareCiamUserId: null,
+ handledare: null,
recievedTimestamp: faker.date.recent(),
hasAvbrott: currentDeltagare.hasAvbrott,
});