feat(avrop): Added additional functionality to avrop-flow. (TV-411)

Squashed commit of the following:

commit b174dd7480baa5e5e4b7f4bea4c9fc674d344c0c
Merge: e1f9d2d 0661d22
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Thu Aug 26 11:50:11 2021 +0200

    Merged develop and resolved conflicts

commit e1f9d2d49e279e704b760a3cbe45941cdcfb81d9
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Thu Aug 26 11:41:45 2021 +0200

    Now fetching handledare and patching through API

commit cc017fdc6eb5d9620399eee011341a0307fe5658
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 25 16:02:20 2021 +0200

    Updated some functionality

commit 345712842c12af08dd4a956d0d2fdfd2592ab3de
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 25 15:32:22 2021 +0200

    Implmented pagination and select all

commit 95f9be3fae6d3f3b258897be9b78f49442ee0747
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 25 13:03:41 2021 +0200

    Renamed avrop-table instances to avrop-list and avrop-table-row to avrop-row

commit 5f1e11bff74c942e2c8b9e62892f043dc299f612
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 25 12:54:56 2021 +0200

    Added some changes to mock-api related to parameter changes inside avrop

commit 71b199744a31b2a4b8bcaa6870094fd900851030
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 25 12:23:57 2021 +0200

    added qp to avrop requests

commit dc0e34b4971ddfd3d683d482502439b961df8852
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 16:02:31 2021 +0200

    Renamed multiple variablenames inside avrop-api

commit 8a4d5471cf637db7d90c6659c893f6841ec9c961
Merge: be9e9b3 50a83f7
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 13:57:50 2021 +0200

    Merged develop and fixed conflicts

commit be9e9b323aee76493b5035cd79f6058781ae4c1a
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 13:27:16 2021 +0200

    Moved around elements inside avrop component

commit 7ede2d00cd7ed105ef12be88e2ab788841329f00
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 09:34:57 2021 +0200

    Moved around some components and other files to match project-structure

commit 7d1396216de643388a5690f2fa2733f127623b6c
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 09:04:28 2021 +0200

    Fixed issues with utforande verksamheter model and mock-data

commit 22baca18c25bd4ce8dcc713e91126214882cf017
Merge: 4ba3c1c 59ce393
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 08:56:02 2021 +0200

    Merged develop and fixed conflicts

commit 4ba3c1ce9dac206602de9651a98aecfd5857a0e5
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 08:48:21 2021 +0200

    Fixed issues with tjanst model and mock-data

commit 50d8c698778fa64cedd4249f6852715d038b450c
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 24 08:40:25 2021 +0200

    Fixed issues with kommun-model and mock-data
This commit is contained in:
Erik Tiekstra
2021-08-26 13:46:17 +02:00
parent 0661d2209b
commit 1f26e80cb3
68 changed files with 2331 additions and 1992 deletions

View File

@@ -39,7 +39,9 @@
</td>
<td>
<ng-container *ngIf="employee.utforandeVerksamheter.length">
{{ employee.utforandeVerksamheter[0]['namn'] }}<ng-container *ngIf="employee.utforandeVerksamheter.length > 1">
{{ employee.utforandeVerksamheter[0]['namn'] }}<ng-container
*ngIf="employee.utforandeVerksamheter.length > 1"
>
(+{{employee.utforandeVerksamheter.length - 1}})</ng-container
>
</ng-container>
@@ -74,9 +76,9 @@
</digi-ng-dialog>
<digi-navigation-pagination
*ngIf="totalPages > 1"
*ngIf="totalPage > 1"
class="employees-list__pagination"
[afTotalPages]="totalPages"
[afTotalPages]="totalPage"
[afCurrentResultStart]="currentResultStart"
[afCurrentResultEnd]="currentResultEnd"
[afTotalResults]="count"

View File

@@ -32,7 +32,7 @@ describe('EmployeesListComponent', () => {
describe('20 employees sorted by Full name Ascending', () => {
beforeEach(() => {
component.employees = employeesMock;
component.paginationMeta = { count: employeesMock.length, limit: 50, page: 1, totalPages: 3 };
component.paginationMeta = { count: employeesMock.length, limit: 50, page: 1, totalPage: 3 };
component.sort = { key: <keyof EmployeeCompactResponse>'fullName', order: SortOrder.ASC };
fixture.detectChanges();

View File

@@ -27,7 +27,7 @@ export class EmployeesListComponent implements OnDestroy {
employeeSelected$: Observable<EmployeeCompact> = this._employeeSelected$.asObservable();
showDialog: boolean;
constructor(private employeeService: EmployeeService) { }
constructor(private employeeService: EmployeeService) {}
columnHeaders: { label: string; key: keyof EmployeeCompactResponse }[] = [
{ label: 'Namn', key: 'name' },
@@ -47,8 +47,8 @@ export class EmployeesListComponent implements OnDestroy {
return this.paginationMeta.page;
}
get totalPages(): number {
return this.paginationMeta?.totalPages;
get totalPage(): number {
return this.paginationMeta?.totalPage;
}
get count(): number {
@@ -73,7 +73,8 @@ export class EmployeesListComponent implements OnDestroy {
}
onDeleteEmployee(employee: EmployeeCompact): void {
this.employeeService.deleteEmployee(employee.id)
this.employeeService
.deleteEmployee(employee.id)
.pipe(takeUntil(this.componentDestroyed$))
.subscribe({
next: (res: DeleteEmployeeMockApiResponse) => {
@@ -87,11 +88,10 @@ export class EmployeesListComponent implements OnDestroy {
},
error: err => {
console.log(err);
}
},
});
}
openDialog(val: boolean, employee: EmployeeCompact): void {
if (!val) {
this.showDialog = false;

View File

@@ -1,17 +0,0 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { AvropService } from './avrop.service';
describe('AvropServiceService', () => {
let service: AvropService;
beforeEach(() => {
TestBed.configureTestingModule({ imports: [HttpClientTestingModule] });
service = TestBed.inject(AvropService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -1,98 +1,120 @@
<msfa-layout>
<section class="call-off" *ngIf="currentStep$ | async; let currentStep; else: loadingRef">
<section class="avrop" *ngIf="currentStep$ | async as currentStep; else: loadingRef">
<digi-typography>
<h2>Välj deltagare att tilldela</h2>
<p>Steg {{ currentStep }} av {{ steps }}:</p>
<header class="avrop__header">
<h1>Nya deltagare</h1>
<p>
Inkomna deltagare kan hanteras enskilt eller gemensamt. Genom att klicka i boxarna för en eller flera
deltagare kan du gå vidare och tilldela ansvarig handledare och skicka välkomstbrev.
</p>
<digi-notification-alert
*ngIf="currentStep === 4 && selectedHandledare$ | async as selectedHandledare"
af-heading="Allt gick bra"
af-variation="success"
>
<p>Tilldelningen är nu skickad till {{selectedHandledare.fullName}}.</p>
</digi-notification-alert>
</header>
<main class="avrop__steps">
<div class="avrop__step-header">
<h2 class="avrop__sub-heading">
<ng-container [ngSwitch]="currentStep">
<ng-container *ngSwitchCase="1">Välj deltagare att tilldela</ng-container>
<ng-container *ngSwitchCase="2">Tilldela handledare</ng-container>
<ng-container *ngSwitchCase="3">Förehandsgranska och tilldela</ng-container>
<ng-container *ngSwitchCase="4">Tilldelade delgare</ng-container>
</ng-container>
</h2>
<div class="avrop__progress-bar" *ngIf="currentStep < 4">
<span>Steg {{ currentStep }} av {{ totalAmountOfSteps }}:</span>
<digi-ng-progress-progressbar
[afSteps]="totalAmountOfSteps"
[afActiveStep]="currentStep"
></digi-ng-progress-progressbar>
</div>
</div>
<div class="avrop__content">
<div class="avrop__filter" *ngIf="currentStep === 1">
<h3>Filter</h3>
<msfa-avrop-filters></msfa-avrop-filters>
</div>
<div class="avrop__select-handledare" *ngIf="currentStep === 2">
<ng-container *ngIf="availableHandledare$ | async as availableHandledare; else loadingRef">
<digi-form-select
*ngIf="availableHandledare?.length; else noAvailabeHandledare"
af-label="Välj handledare att tilldela"
af-placeholder="Välj handledare"
[afRequired]="true"
(afOnChange)="changeHandledare($event.detail)"
>
<option
*ngFor="let availableHandledare of availableHandledare"
[value]="availableHandledare.ciamUserId"
>
{{ availableHandledare.fullName }}
</option>
</digi-form-select>
<ng-template #noAvailableHandledare>
<p>Inga handledare har behörighet till markerade deltagare</p>
</ng-template>
</ng-container>
</div>
<h3>Välj deltagare att tilldela handledare</h3>
<msfa-avrop-list
*ngIf="avropData$ | async as avropData"
[availableAvrop]="avropData.data"
[paginationMeta]="avropData.meta"
[selectedAvrop]="selectedAvrop$ | async"
[isLocked]="avropIsLocked$ | async"
[isSubmitted]="avropIsSubmitted$ | async"
[handledare]="selectedHandledare$ | async"
[handledareConfirmed]="handledareConfirmed$ | async"
(selectionChanged)="updateSelectedAvrop($event)"
(paginated)="setNewPage($event)"
></msfa-avrop-list>
</div>
<div class="avrop__footer" [ngSwitch]="currentStep">
<digi-notification-alert
*ngIf="error$ | async as error"
af-heading="Felmeddelande"
af-variation="danger"
af-closeable="true"
(afOnClose)="resetError()"
>
<p>{{error}}</p>
</digi-notification-alert>
<div class="avrop__cta-wrapper">
<ng-container *ngSwitchCase="1">
<digi-button af-size="m" (afOnClick)="lockSelectedAvrop()">Nästa</digi-button>
</ng-container>
<ng-container *ngSwitchCase="2">
<digi-button af-variation="secondary" af-size="m" (afOnClick)="unlockSelectedAvrop()"
>Tillbaka</digi-button
>
<digi-button af-size="m" (afOnClick)="confirmHandledare()">Tilldela</digi-button>
</ng-container>
<ng-container *ngSwitchCase="3">
<digi-button af-variation="secondary" af-size="m" (afOnClick)="unconfirmHandledare()"
>Tillbaka</digi-button
>
<digi-button af-size="m" (afOnClick)="save()">Bekräfta tilldelning</digi-button>
</ng-container>
<ng-container *ngSwitchCase="4">
<digi-button af-size="m" (afOnClick)="returnToStep1()">Tillbaka till nya deltagare</digi-button>
</ng-container>
</div>
</div>
</main>
</digi-typography>
<digi-ng-progress-progressbar [afSteps]="steps" afAriaLabel="An aria label" [afActiveStep]="currentStep">
</digi-ng-progress-progressbar>
<div>
<ng-container *ngIf="currentStep == 4">
<h2>Avropet är sparat</h2>
<digi-button af-size="m" class="employee-form__read-more" (afOnClick)="goToStep1()">
Tillbaka till nya deltagare
</digi-button>
</ng-container>
<ng-container *ngIf="currentStep == 1">
<msfa-avrop-filters></msfa-avrop-filters>
</ng-container>
<ng-container *ngIf="currentStep == 3">
<h2>Vänligen bekräfta</h2>
</ng-container>
<ng-container *ngIf="currentStep < 4">
<msfa-avrop-table
[selectableDeltagareList]="selectableDeltagareList$ | async"
[selectedDeltagareListInput]="selectedDeltagareList$ | async"
[isLocked]="deltagareListIsLocked$ | async"
(changedSelectedDeltagareList)="updateSelectedDeltagareList($event)"
[handledare]="selectedHandledare$ | async"
[handledareConfirmed]="handledareConfirmed$ | async"
></msfa-avrop-table>
</ng-container>
<ng-container *ngIf="currentStep == 1">
<digi-button af-size="m" class="employee-form__read-more" (afOnClick)="lockSelectedDeltagare()">
Lås deltagare
</digi-button>
</ng-container>
<ng-container *ngIf="currentStep == 2">
<h2>Välj handledare</h2>
<ng-container *ngIf="selectableHandledareList$ | async; let selectableHandledareList; else loadingRefSmall">
<select
[value]="(selectedHandledare$ | async)?.id ? (selectedHandledare$ | async)?.id : ''"
(change)="changeHandledare($event)"
>
<option disabled value="">Välj handledare</option>
<option *ngFor="let selectableHandledare of selectableHandledareList" [value]="selectableHandledare?.id">
{{ selectableHandledare?.fullName }}
</option>
</select>
<span *ngIf="selectableHandledareList.length === 0"
>Inga handledare har behörighet till alla markerade deltagare</span
>
</ng-container>
<br /><br />
<digi-button
af-variation="secondary"
af-size="m"
class="employee-form__read-more"
(afOnClick)="unlockSelectedDeltagare()"
>
Tillbaka
</digi-button>
<digi-button af-size="m" class="employee-form__read-more" (afOnClick)="confirmHandledare()">
Tilldela
</digi-button>
</ng-container>
<div *ngIf="currentStep == 3">
<br /><br />
<digi-button
af-variation="secondary"
af-size="m"
class="employee-form__read-more"
(afOnClick)="unconfirmHandledare()"
>
Tillbaka
</digi-button>
<digi-button af-size="m" class="employee-form__read-more" (afOnClick)="save()"> Spara avrop </digi-button>
</div>
</div>
</section>
<ng-template #loadingRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar personal"></digi-ng-skeleton-base>
<digi-icon-spinner class="msfa__spinner" af-title="Laddar innehåll"></digi-icon-spinner>
</ng-template>
<ng-template #loadingRefSmall>
<digi-icon-spinner af-title="Laddar innehåll"></digi-icon-spinner>
</ng-template>
<hr />
</msfa-layout>

View File

@@ -0,0 +1,36 @@
@import 'variables/gutters';
.avrop {
&__header {
margin-bottom: $digi--layout--gutter--xxl;
}
&__sub-heading {
margin: 0;
}
&__step-header {
display: flex;
justify-content: space-between;
align-items: start;
margin-bottom: $digi--layout--gutter--xl;
}
&__select-handledare {
max-width: var(--digi--typography--text--max-width);
margin-bottom: $digi--layout--gutter--xl;
}
&__footer {
display: flex;
flex-direction: column;
gap: $digi--layout--gutter--xl;
max-width: var(--digi--typography--text--max-width);
margin-top: $digi--layout--gutter--xl;
}
&__cta-wrapper {
display: flex;
gap: var(--digi--layout--gutter);
}
}

View File

@@ -3,20 +3,27 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { AvropComponent } from './avrop.component';
import { AvropFiltersComponent } from './components/avrop-filters/avrop-filters.component';
import { AvropTableComponent } from './components/avrop-table/avrop-table.component';
import { AvropFiltersModule } from './components/avrop-filters/avrop-filters.module';
import { AvropListModule } from './components/avrop-list/avrop-list.module';
describe('CallOffComponent', () => {
describe('AvropComponent', () => {
let component: AvropComponent;
let fixture: ComponentFixture<AvropComponent>;
beforeEach(
waitForAsync(() => {
void TestBed.configureTestingModule({
declarations: [AvropComponent, LayoutComponent, AvropFiltersComponent, AvropTableComponent],
imports: [RouterTestingModule, HttpClientTestingModule, DigiNgProgressProgressbarModule],
declarations: [AvropComponent],
imports: [
RouterTestingModule,
HttpClientTestingModule,
DigiNgProgressProgressbarModule,
LayoutModule,
AvropFiltersModule,
AvropListModule,
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
})

View File

@@ -1,9 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { Avrop, AvropCompactData } from '@msfa-models/avrop.model';
import { Handledare } from '@msfa-models/handledare.model';
import { MultiselectFilterOption } from '@msfa-models/multiselect-filter-option';
import { AvropService } from '@msfa-services/avrop.service';
import { Observable } from 'rxjs';
import { AvropService } from './avrop.service';
import { HandledareAvrop } from './models/handledare-avrop';
@Component({
selector: 'msfa-avrop',
@@ -12,31 +12,32 @@ import { HandledareAvrop } from './models/handledare-avrop';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropComponent {
steps = 3;
readonly totalAmountOfSteps = 3;
currentStep$: Observable<number> = this.avropService.currentStep$;
currentStep$ = this.avropService.currentStep$;
selectedUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.selectedUtforandeVerksamheter$;
selectableDeltagareList$: Observable<Avrop[]> = this.avropService.selectableDeltagareList$;
selectedDeltagareList$: Observable<Avrop[]> = this.avropService.selectedDeltagareList$;
selectableHandledareList$: Observable<HandledareAvrop[]> = this.avropService.selectableHandledareList$;
selectedHandledare$: Observable<HandledareAvrop> = this.avropService.selectedHandledare$;
deltagareListIsLocked$: Observable<boolean> = this.avropService.deltagareListIsLocked$;
error$: Observable<string> = this.avropService.error$;
filteredUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.filteredUtforandeVerksamheter$;
avropData$: Observable<AvropCompactData> = this.avropService.avropData$;
selectedAvrop$: Observable<Avrop[]> = this.avropService.selectedAvrop$;
availableHandledare$: Observable<Handledare[]> = this.avropService.availableHandledare$;
selectedHandledare$: Observable<Handledare> = this.avropService.selectedHandledare$;
avropIsLocked$: Observable<boolean> = this.avropService.avropIsLocked$;
handledareConfirmed$: Observable<boolean> = this.avropService.handledareIsConfirmed$;
avropIsSubmitted$: Observable<boolean> = this.avropService.avropIsSubmitted$;
constructor(private avropService: AvropService) {}
updateSelectedDeltagareList(deltagareList: Avrop[]): void {
this.avropService.setSelectedDeltagare(deltagareList);
updateSelectedAvrop(deltagareList: Avrop[]): void {
this.avropService.setSelectedAvrop(deltagareList);
}
lockSelectedDeltagare(): void {
this.avropService.lockSelectedDeltagare();
lockSelectedAvrop(): void {
this.avropService.lockSelectedAvrop();
}
unlockSelectedDeltagare(): void {
this.avropService.unlockSelectedDeltagare();
unlockSelectedAvrop(): void {
this.avropService.unlockSelectedAvrop();
}
confirmHandledare(): void {
@@ -51,13 +52,19 @@ export class AvropComponent {
return this.avropService.save();
}
changeHandledare(newHandledare: { target: HTMLInputElement }): void {
const handledareId = newHandledare.target.value;
this.avropService.setHandledareState(handledareId);
changeHandledare(handledareId: string): void {
this.avropService.assignHandledare(handledareId);
}
goToStep1(): void {
returnToStep1(): void {
this.avropService.goToStep1();
}
resetError(): void {
this.avropService.resetError();
}
setNewPage(page: number): void {
this.avropService.setPage(page);
}
}

View File

@@ -5,24 +5,18 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
import { AvropComponent } from './avrop.component';
import { AvropFiltersComponent } from './components/avrop-filters/avrop-filters.component';
import { TemporaryFilterComponent } from './components/avrop-filters/temporary-filter/temporary-filter.component';
import { AvropTableRowComponent } from './components/avrop-table/avrop-table-row/avrop-table-row.component';
import { AvropTableComponent } from './components/avrop-table/avrop-table.component';
import { AvropFiltersModule } from './components/avrop-filters/avrop-filters.module';
import { AvropListModule } from './components/avrop-list/avrop-list.module';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [
AvropComponent,
AvropFiltersComponent,
AvropTableComponent,
AvropTableRowComponent,
TemporaryFilterComponent,
],
declarations: [AvropComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: AvropComponent }]),
LayoutModule,
AvropListModule,
AvropFiltersModule,
DigiNgProgressProgressbarModule,
DigiNgSkeletonBaseModule,
],

View File

@@ -1,194 +0,0 @@
import { Injectable } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { MultiselectFilterOption } from '@msfa-models/multiselect-filter-option';
import { AvropApiService } from '@msfa-services/api/avrop-api.service';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
import { HandledareAvrop } from './models/handledare-avrop';
type Step = 1 | 2 | 3 | 4;
@Injectable({
providedIn: 'root',
})
export class AvropService {
private _selectedTjanster$ = new BehaviorSubject<MultiselectFilterOption[]>(null);
private _selectedUtforandeVerksamheter$ = new BehaviorSubject<MultiselectFilterOption[]>(null);
private _selectedKommuner$ = new BehaviorSubject<MultiselectFilterOption[]>(null);
private _selectedDeltagareList$ = new BehaviorSubject<Avrop[]>([]);
private _deltagareListIsLocked$ = new BehaviorSubject<boolean>(null);
private _selectedHandledare$ = new BehaviorSubject<HandledareAvrop>(null);
private _handledareIsConfirmed$ = new BehaviorSubject<boolean>(false);
private _avropIsSaved$ = new BehaviorSubject<boolean>(false);
selectedTjanster$: Observable<MultiselectFilterOption[]> = this._selectedTjanster$.asObservable();
selectedUtforandeVerksamheter$: Observable<
MultiselectFilterOption[]
> = this._selectedUtforandeVerksamheter$.asObservable();
selectedKommuner$: Observable<MultiselectFilterOption[]> = this._selectedKommuner$.asObservable();
selectableDeltagareList$: Observable<Avrop[]> = combineLatest([
this.selectedTjanster$,
this.selectedUtforandeVerksamheter$,
this.selectedKommuner$,
]).pipe(
switchMap(([selectedTjanster, selectedUtforandeVerksamheter, selectedKommuner]) =>
this.avropApiService.getNyaAvrop$(selectedTjanster, selectedKommuner, selectedUtforandeVerksamheter)
)
);
selectableTjanster$: Observable<MultiselectFilterOption[]> = combineLatest([
this.selectedUtforandeVerksamheter$,
this.selectedKommuner$,
]).pipe(
switchMap(([selectedUtforandeVerksamheter, selectedKommuner]) =>
this.avropApiService.getSelectableTjanster$(selectedKommuner, selectedUtforandeVerksamheter)
)
);
selectableUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = combineLatest([
this.selectedTjanster$,
this.selectedKommuner$,
]).pipe(
switchMap(([selectedTjanster, selectedKommuner]) =>
this.avropApiService.getSelectableUtforandeVerksamheter$(selectedTjanster, selectedKommuner)
)
);
selectableKommuner$: Observable<MultiselectFilterOption[]> = combineLatest([
this.selectedTjanster$,
this.selectedUtforandeVerksamheter$,
]).pipe(
switchMap(([selectedTjanster, selectedUtforandeVerksamheter]) =>
this.avropApiService.getSelectableKommuner$(selectedTjanster, selectedUtforandeVerksamheter)
)
);
selectedDeltagareList$: Observable<Avrop[]> = this._selectedDeltagareList$.asObservable();
deltagareListIsLocked$: Observable<boolean> = this._deltagareListIsLocked$.asObservable();
lockedDeltagareList$: Observable<Avrop[]> = combineLatest([
this.selectedDeltagareList$,
this.deltagareListIsLocked$,
]).pipe(map(([selectedDeltagareList, isLocked]) => (isLocked ? selectedDeltagareList : null)));
selectableHandledareList$: Observable<HandledareAvrop[]> = this.lockedDeltagareList$.pipe(
switchMap(lockedDeltagare => this.avropApiService.getSelectableHandledare$(lockedDeltagare))
);
selectedHandledare$: Observable<HandledareAvrop> = this._selectedHandledare$.asObservable();
handledareIsConfirmed$: Observable<boolean> = this._handledareIsConfirmed$.asObservable();
avropIsSaved$: Observable<boolean> = this._handledareIsConfirmed$.asObservable();
currentStep$: Observable<Step> = combineLatest([
this.handledareIsConfirmed$,
this._deltagareListIsLocked$,
this.avropIsSaved$,
]).pipe(
map(([confirmedHandledare, lockedDeltagareList, avropIsSaved]) =>
AvropService.calculateStep(confirmedHandledare, lockedDeltagareList, avropIsSaved)
)
);
private static calculateStep(
confirmedHandledare: boolean,
deltagareListIsLocked: boolean,
avropIsSaved: boolean
): Step {
if (avropIsSaved && confirmedHandledare && deltagareListIsLocked) {
return 4;
}
if (confirmedHandledare && deltagareListIsLocked) {
return 3;
}
if (deltagareListIsLocked) {
return 2;
}
return 1;
}
setSelectedDeltagare(deltagare: Avrop[]): void {
this._selectedDeltagareList$.next(deltagare);
}
constructor(private avropApiService: AvropApiService) {}
lockSelectedDeltagare(): void {
if ((this._selectedDeltagareList$?.value?.length ?? -1) <= 0) {
throw new Error('För att låsa deltagare behöver några ha markerats först.');
}
this._deltagareListIsLocked$.next(true);
}
unlockSelectedDeltagare(): void {
this._deltagareListIsLocked$.next(false);
}
confirmHandledare(): void {
if (!this._selectedHandledare$?.value) {
throw new Error('För att kunna tilldela behövs en handledare väljas först.');
}
this._handledareIsConfirmed$.next(true);
}
unconfirmHandledare(): void {
this._handledareIsConfirmed$.next(false);
}
async save(): Promise<void> {
if (!this._handledareIsConfirmed$) {
throw new Error('Handledaren måste bekräftas innan avropet kan sparas');
}
if (!this._deltagareListIsLocked$) {
throw new Error('Deltagarlistan måste låsas innan avropet kan sparas');
}
await this.avropApiService.tilldelaHandledare(this._selectedDeltagareList$.value, this._selectedHandledare$.value);
this._avropIsSaved$.next(true);
return;
}
setHandledareState(handledareId: string): void {
this.selectableHandledareList$.pipe(first()).subscribe(handledareList => {
this._selectedHandledare$.next(handledareList.find(handledare => handledare.id === handledareId));
});
}
setSelectedTjanster(selectedFilterOptions: MultiselectFilterOption[]): void {
this._selectedTjanster$.next(selectedFilterOptions);
}
setSelectedUtforandeVerksamheter(selectedFilterOptions: MultiselectFilterOption[]): void {
this._selectedUtforandeVerksamheter$.next(selectedFilterOptions);
}
setSelectedKommuner(selectedFilterOptions: MultiselectFilterOption[]): void {
this._selectedKommuner$.next(selectedFilterOptions);
}
goToStep1(): void {
this._selectedHandledare$.next(null);
this._selectedDeltagareList$.next([]);
this._deltagareListIsLocked$.next(false);
this._handledareIsConfirmed$.next(false);
}
removeKommun(kommunToRemove: MultiselectFilterOption) {
this.setSelectedKommuner(this._selectedKommuner$.value.filter(selectedKommun => selectedKommun !== kommunToRemove));
}
removeUtforandeVerksamhet(utforandeVerksamhetToRemove: MultiselectFilterOption) {
this.setSelectedUtforandeVerksamheter(
this._selectedUtforandeVerksamheter$.value.filter(
selectedUtforandeVerksamhet => selectedUtforandeVerksamhet !== utforandeVerksamhetToRemove
)
);
}
removeTjanst(tjanstToRemove: MultiselectFilterOption) {
this.setSelectedTjanster(this._selectedTjanster$.value.filter(selectedTjanst => selectedTjanst !== tjanstToRemove));
}
}

View File

@@ -1,36 +1,36 @@
<div style="display: flex">
<ng-container *ngIf="selectableTjanster$ | async; let selectableTjanster; else loadingRef">
<ng-container *ngIf="availableTjanster$ | async; let availableTjanster; else loadingRef">
<msfa-temporary-filter
[filterLabel]="'Tjänster'"
[filterOptions]="selectableTjanster"
[selectedOptions]="selectedTjanster$ | async"
[filterOptions]="availableTjanster"
[selectedOptions]="filteredTjanster$ | async"
(selectedOptionsChange)="updateSelectedTjanster($event)"
></msfa-temporary-filter>
</ng-container>
<ng-container *ngIf="selectableUtforandeVerksamheter$ | async; let selectableUtforandeVerksamheter; else loadingRef">
<ng-container *ngIf="availableUtforandeVerksamheter$ | async; let availableUtforandeVerksamheter; else loadingRef">
<msfa-temporary-filter
[filterLabel]="'Utförande verksamheter'"
[filterOptions]="selectableUtforandeVerksamheter"
[selectedOptions]="selectedUtforandeVerksamheter$ | async"
[filterOptions]="availableUtforandeVerksamheter"
[selectedOptions]="filteredUtforandeVerksamheter$ | async"
(selectedOptionsChange)="updateSelectedUtforandeVerksamheter($event)"
></msfa-temporary-filter>
</ng-container>
<ng-container *ngIf="selectableKommuner$ | async; let selectableKommuner; else loadingRef">
<ng-container *ngIf="availableKommuner$ | async; let availableKommuner; else loadingRef">
<msfa-temporary-filter
[filterLabel]="'Kommuner'"
[filterOptions]="selectableKommuner"
[selectedOptions]="selectedKommuner$ | async"
[filterOptions]="availableKommuner"
[selectedOptions]="filteredKommuner$ | async"
(selectedOptionsChange)="updateSelectedKommuner($event)"
></msfa-temporary-filter>
</ng-container>
</div>
<br /><br />
<div class="avrop-filters__tags">
<div class="avrop-filters__tag" *ngFor="let kommun of selectedKommuner$ | async">
<div class="avrop-filters__tag" *ngFor="let kommun of filteredKommuner$ | async">
<digi-tag [afText]="kommun.label" (click)="removeKommun(kommun)" af-no-icon="false" af-size="s"></digi-tag>
</div>
<div class="avrop-filters__tag" *ngFor="let kommun of selectedUtforandeVerksamheter$ | async">
<div class="avrop-filters__tag" *ngFor="let kommun of filteredUtforandeVerksamheter$ | async">
<digi-tag
[afText]="kommun.label"
(click)="removeUtforandeVerksamhet(kommun)"
@@ -38,7 +38,7 @@
af-size="s"
></digi-tag>
</div>
<div class="avrop-filters__tag" *ngFor="let kommun of selectedTjanster$ | async">
<div class="avrop-filters__tag" *ngFor="let kommun of filteredTjanster$ | async">
<digi-tag [afText]="kommun.label" (click)="removeTjanst(kommun)" af-no-icon="false" af-size="s"></digi-tag>
</div>
</div>

View File

@@ -1,12 +1,11 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { AvropService } from '../../avrop.service';
import { AvropFiltersComponent } from './avrop-filters.component';
import { TemporaryFilterComponent } from './temporary-filter/temporary-filter.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { AvropService } from '../../avrop.service';
import { of } from 'rxjs';
import { By } from '@angular/platform-browser';
import { HttpClientTestingModule } from '@angular/common/http/testing';
describe('AvropFiltersComponent', () => {
let component: AvropFiltersComponent;
@@ -30,8 +29,8 @@ describe('AvropFiltersComponent', () => {
expect(component).toBeTruthy();
});
it('should show 1 tag if selectedKommuner$ is an observable with one value', () => {
component.selectedKommuner$ = of([{ id: '1', label: 'Stockholm', count: 1 }]);
it('should show 1 tag if filteredKommuner$ is an observable with one value', () => {
component.filteredKommuner$ = of([{ id: '1', label: 'Stockholm', count: 1 }]);
fixture.detectChanges();
const tags = fixture.debugElement.queryAll(By.css('.avrop-filters__tag'));
expect(tags.length).toBe(1);
@@ -39,7 +38,7 @@ describe('AvropFiltersComponent', () => {
it('clicking a kommun-tag should trigger removeKommun()', fakeAsync(() => {
jest.spyOn(component, 'removeKommun').mockReturnThis();
component.selectedKommuner$ = of([{ id: '1', label: 'Stockholm', count: 1 }]);
component.filteredKommuner$ = of([{ id: '1', label: 'Stockholm', count: 1 }]);
fixture.detectChanges();
const tags = fixture.debugElement.query(By.css('digi-tag'));
tags.nativeElement.click();

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MultiselectFilterOption } from '@msfa-models/multiselect-filter-option';
import { AvropService } from '@msfa-services/avrop.service';
import { Observable } from 'rxjs';
import { AvropService } from '../../avrop.service';
@Component({
selector: 'msfa-avrop-filters',
@@ -10,14 +10,14 @@ import { AvropService } from '../../avrop.service';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropFiltersComponent {
selectableTjanster$: Observable<MultiselectFilterOption[]> = this.avropService.selectableTjanster$;
selectableUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.selectableUtforandeVerksamheter$;
selectableKommuner$: Observable<MultiselectFilterOption[]> = this.avropService.selectableKommuner$;
selectedTjanster$: Observable<MultiselectFilterOption[]> = this.avropService.selectedTjanster$;
selectedUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.selectedUtforandeVerksamheter$;
selectedKommuner$: Observable<MultiselectFilterOption[]> = this.avropService.selectedKommuner$;
availableTjanster$: Observable<MultiselectFilterOption[]> = this.avropService.availableTjanster$;
availableUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.availableUtforandeVerksamheter$;
availableKommuner$: Observable<MultiselectFilterOption[]> = this.avropService.availableKommuner$;
filteredTjanster$: Observable<MultiselectFilterOption[]> = this.avropService.filteredTjanster$;
filteredUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = this.avropService
.filteredUtforandeVerksamheter$;
filteredKommuner$: Observable<MultiselectFilterOption[]> = this.avropService.filteredKommuner$;
constructor(private avropService: AvropService) {}

View File

@@ -0,0 +1,12 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { TemporaryFilterModule } from '../temporary-filter/temporary-filter.module';
import { AvropFiltersComponent } from './avrop-filters.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [AvropFiltersComponent],
imports: [CommonModule, TemporaryFilterModule],
exports: [AvropFiltersComponent],
})
export class AvropFiltersModule {}

View File

@@ -0,0 +1,36 @@
<div class="avrop-list">
<div class="avrop-list__select-all">
<digi-form-checkbox
*ngIf="!isLocked"
af-label="Välj alla deltagare"
[afChecked]="isAllSelected"
(change)="toggleAllAvrop($event.target.checked)"
></digi-form-checkbox>
</div>
<ul class="avrop-list__list">
<li *ngFor="let avrop of avropRows">
<msfa-avrop-row
[avrop]="avrop"
[isSelected]="isSelected(avrop)"
[isLocked]="isLocked"
[isSubmitted]="isSubmitted"
[handledare]="handledare"
[handledareConfirmed]="handledareConfirmed"
(toggled)="toggleSelectedAvrop(avrop, $event)"
(deleted)="toggleSelectedAvrop(avrop, false)"
></msfa-avrop-row>
</li>
</ul>
<digi-navigation-pagination
*ngIf="totalPage > 1 && !isLocked"
class="avrop-list__pagination"
[afTotalPages]="totalPage"
[afCurrentResultStart]="currentResultStart"
[afCurrentResultEnd]="currentResultEnd"
[afTotalResults]="count"
(afOnPageChange)="emitNewPage($event.detail)"
af-result-name="deltagare"
>
</digi-navigation-pagination>
</div>

View File

@@ -0,0 +1,19 @@
@import 'mixins/list';
.avrop-list {
&__select-all {
padding: var(--digi--layout--gutter);
}
&__list {
@include msfa__reset-list;
display: flex;
flex-direction: column;
gap: 1rem;
}
&__pagination {
display: block;
margin-top: var(--digi--layout--gutter);
}
}

View File

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

View File

@@ -0,0 +1,76 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { Avrop, AvropCompact } from '@msfa-models/avrop.model';
import { Handledare } from '@msfa-models/handledare.model';
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
@Component({
selector: 'msfa-avrop-list',
templateUrl: './avrop-list.component.html',
styleUrls: ['./avrop-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropListComponent {
@Input() availableAvrop: AvropCompact[];
@Input() paginationMeta: PaginationMeta;
@Input() selectedAvrop: AvropCompact[];
@Input() handledare: Handledare;
@Input() isLocked: boolean;
@Input() isSubmitted: boolean;
@Input() handledareConfirmed: boolean;
@Output() selectionChanged = new EventEmitter<AvropCompact[]>();
@Output() paginated = new EventEmitter<number>();
get avropRows(): AvropCompact[] {
return this.isLocked ? this.selectedAvrop : this.availableAvrop;
}
get isAllSelected(): boolean {
return this.selectedAvrop?.length === this.availableAvrop?.length;
}
get currentPage(): number {
return this.paginationMeta.page;
}
get totalPage(): number {
return this.paginationMeta?.totalPage;
}
get count(): number {
return this.paginationMeta.count;
}
get currentResultStart(): number {
return (this.currentPage - 1) * this.paginationMeta.limit + 1;
}
get currentResultEnd(): number {
const end = this.currentResultStart + this.paginationMeta.limit - 1;
return end < this.count ? end : this.count;
}
isSelected(avrop: Avrop): boolean {
return !!this.selectedAvrop?.find(selectedAvrop => selectedAvrop.id === avrop.id);
}
toggleAllAvrop(selected: boolean): void {
if (selected && this.selectedAvrop?.length !== this.availableAvrop?.length) {
this.selectionChanged.emit(this.availableAvrop);
} else if (!selected && this.selectedAvrop.length) {
this.selectionChanged.emit([]);
}
}
toggleSelectedAvrop(avrop: Avrop, selected: boolean): void {
const avropIsSelected = !!this.selectedAvrop?.find(selectedAvrop => selectedAvrop.id === avrop.id);
if (selected && !avropIsSelected) {
this.selectionChanged.emit([...this.selectedAvrop, avrop]);
} else if (!selected && avropIsSelected) {
this.selectionChanged.emit(this.selectedAvrop.filter(selectedAvrop => selectedAvrop.id !== avrop.id));
}
}
emitNewPage(page: number): void {
this.paginated.emit(page);
}
}

View File

@@ -0,0 +1,12 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AvropRowModule } from '../avrop-row/avrop-row.module';
import { AvropListComponent } from './avrop-list.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [AvropListComponent],
imports: [CommonModule, AvropRowModule],
exports: [AvropListComponent],
})
export class AvropListModule {}

View File

@@ -0,0 +1,68 @@
<digi-typography>
<div
class="avrop-row"
[ngClass]="{'avrop-row--locked': isLocked, 'avrop-row--deletable': handledareConfirmed && !isSubmitted}"
>
<div class="avrop-row__select" *ngIf="!isLocked">
<digi-form-checkbox
af-label="Välj deltagare"
[afChecked]="isSelected"
(change)="emitToggle($event.target.checked)"
></digi-form-checkbox>
</div>
<dl class="avrop-row__name">
<dt class="avrop-table__label">Namn:</dt>
<dd *ngIf="avrop.fullName; else emptyText">{{avrop.fullName}}</dd>
</dl>
<dl class="avrop-row__tjanst">
<dt class="avrop-table__label">Tjänst:</dt>
<dd *ngIf="avrop.tjanst; else emptyText">{{avrop.tjanst}}</dd>
</dl>
<dl class="avrop-row__start">
<dt class="avrop-table__label">Startdatum:</dt>
<dd>
<digi-typography-time
*ngIf="avrop.startDate; else emptyText"
[afDateTime]="avrop.startDate"
></digi-typography-time>
</dd>
</dl>
<dl class="avrop-row__end">
<dt class="avrop-table__label">Slutdatum:</dt>
<dd>
<digi-typography-time *ngIf="avrop.endDate; else emptyText" [afDateTime]="avrop.endDate"></digi-typography-time>
</dd>
</dl>
<dl class="avrop-row__translator">
<dt class="avrop-table__label">Språkstöd/Tolk:</dt>
<dd>{{avrop.sprakstod || '- '}}/{{avrop.tolkbehov || ' -'}}</dd>
</dl>
<dl class="avrop-row__address">
<dt class="avrop-table__label">Utförande adress:</dt>
<dd *ngIf="avrop.utforandeAdress; else emptyText">{{avrop.utforandeAdress}}</dd>
</dl>
<dl class="avrop-row__level">
<dt class="avrop-table__label">Spår/nivå:</dt>
<dd *ngIf="avrop.trackCode; else emptyText">{{avrop.trackCode}}</dd>
</dl>
<dl class="avrop-row__handledare" *ngIf="isLocked">
<dt class="avrop-table__label">Vald handledare:</dt>
<dd *ngIf="handledare?.fullName; else emptyText">{{handledare.fullName}}</dd>
</dl>
<div *ngIf="isLocked" class="avrop-row__delete">
<digi-button
*ngIf="handledareConfirmed && !isSubmitted"
[attr.af-variation]="ButtonVariation.TERTIARY"
(afOnClick)="emitDelete()"
>
Ta bort
<digi-icon-x slot="icon"></digi-icon-x>
</digi-button>
</div>
</div>
<ng-template #emptyText>
<span aria-hidden="true">-</span>
<span class="msfa__a11y-sr-only">Info saknas</span>
</ng-template>
</digi-typography>

View File

@@ -0,0 +1,97 @@
@import 'variables/gutters';
.avrop-row {
position: relative;
display: grid;
padding: var(--digi--layout--gutter);
background-color: var(--digi--ui--color--background--secondary);
grid-template-columns: auto repeat(4, 1fr);
gap: var(--digi--layout--gutter) $digi--layout--gutter--l;
grid-template-areas:
'select name start translator level'
'select tjanst end address handledare';
&--locked {
grid-template-columns: repeat(4, 1fr) 7.5rem;
grid-template-areas:
'name start translator level delete'
'tjanst end address handledare delete';
}
&__select {
grid-area: select;
margin-right: var(--digi--layout--gutter);
display: flex;
justify-content: center;
align-items: center;
}
&__name {
grid-area: name;
}
&__tjanst {
grid-area: tjanst;
}
&__start {
grid-area: start;
}
&__end {
grid-area: end;
}
&__translator {
grid-area: translator;
}
&__address {
grid-area: address;
}
&__level {
grid-area: level;
}
&__handledare {
grid-area: handledare;
}
&__delete {
grid-area: delete;
display: flex;
justify-content: flex-end;
align-items: flex-start;
}
}
.avrop-row__close-btn {
position: absolute;
top: 0;
right: 0;
::ng-deep {
button {
padding: 0.5rem 0.75rem !important;
}
}
}
.avrop-row__checkbox {
margin-top: 1.5rem;
}
.avrop-row__data-row {
display: flex;
flex-direction: row;
gap: $digi--layout--gutter--xl $digi--layout--gutter--l;
flex-grow: 1;
}
.avrop-row__data-column {
display: flex;
flex-direction: column;
gap: $digi--layout--gutter--l 0;
flex-grow: 1;
}
.avrop-row__data-column--bottom-align {
justify-content: flex-end;
}
.avrop-table__label {
display: block;
}

View File

@@ -1,21 +1,20 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AvropRowComponent } from './avrop-row.component';
import { AvropTableRowComponent } from './avrop-table-row.component';
describe('AvropTableRowComponent', () => {
let component: AvropTableRowComponent;
let fixture: ComponentFixture<AvropTableRowComponent>;
describe('AvropRowComponent', () => {
let component: AvropRowComponent;
let fixture: ComponentFixture<AvropRowComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [AvropTableRowComponent],
declarations: [AvropRowComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AvropTableRowComponent);
fixture = TestBed.createComponent(AvropRowComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -0,0 +1,31 @@
import { ButtonVariation } from '@af/digi-ng/_button/button';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { AvropCompact } from '@msfa-models/avrop.model';
import { Handledare } from '@msfa-models/handledare.model';
@Component({
selector: 'msfa-avrop-row',
templateUrl: './avrop-row.component.html',
styleUrls: ['./avrop-row.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropRowComponent {
@Input() avrop: AvropCompact;
@Input() isSelected: boolean;
@Input() isLocked: boolean;
@Input() isSubmitted: boolean;
@Input() handledare: Handledare;
@Input() handledareConfirmed: boolean;
@Output() toggled = new EventEmitter<boolean>();
@Output() deleted = new EventEmitter<void>();
readonly ButtonVariation = ButtonVariation;
emitToggle(isSelected: boolean): void {
this.toggled.emit(isSelected);
}
emitDelete(): void {
this.deleted.emit();
}
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AvropRowComponent } from './avrop-row.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [AvropRowComponent],
imports: [CommonModule],
exports: [AvropRowComponent],
})
export class AvropRowModule {}

View File

@@ -1,81 +0,0 @@
<div class="avrop-table-row">
<div class="avrop-table-row__data-row">
<div class="avrop-table-row__data-column">
<digi-form-checkbox
*ngIf="!isLocked"
class="avrop-table-row__checkbox"
[afLabel]="'Välj sökande'"
[afChecked]="isSelected"
(change)="emitSelectionChange($event.target.checked)"
>
</digi-form-checkbox>
</div>
<div class="avrop-table-row__data-column">
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Namn:</strong>
<span>{{deltagare?.fullName}}</span>
</digi-typography>
</div>
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Tjänst:</strong>
<span>{{deltagare?.tjanst}}</span>
</digi-typography>
</div>
</div>
<div class="avrop-table-row__data-column">
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Startdatum:</strong>
<digi-typography-time *ngIf="deltagare?.startDate" [afDateTime]="deltagare?.startDate"></digi-typography-time>
</digi-typography>
</div>
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Slutdatum:</strong>
<digi-typography-time *ngIf="deltagare?.endDate" [afDateTime]="deltagare?.endDate"></digi-typography-time>
</digi-typography>
</div>
</div>
<div class="avrop-table-row__data-column">
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Språkstöd/Tolk:</strong>
<span>{{deltagare?.sprakstod + '/' + deltagare?.tolkbehov}}</span>
</digi-typography>
</div>
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Utförande adress:</strong>
<span>{{deltagare?.utforandeAdress}}</span>
</digi-typography>
</div>
</div>
<div class="avrop-table-row__data-column avrop-table-row__data-column--bottom-align">
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Spår/nivå:</strong>
<span>{{deltagare?.trackCode}}</span>
</digi-typography>
</div>
</div>
<div *ngIf="handledare" class="avrop-table-row__data-column avrop-table-row__data-column--bottom-align">
<div class="avrop-table__cell">
<digi-typography>
<strong class="avrop-table__label">Vald handledare:</strong>
<span>{{handledare?.fullName}}</span>
</digi-typography>
</div>
</div>
</div>
<digi-button
class="avrop-table-row__close-btn"
*ngIf="handledareConfirmed"
[attr.af-variation]="ButtonVariation.TERTIARY"
(afOnClick)="emitDeltagareDeleted()"
>
Ta bort
<digi-icon-x slot="icon"></digi-icon-x>
</digi-button>
</div>

View File

@@ -1,47 +0,0 @@
@import 'variables/gutters';
.avrop-table-row {
position: relative;
display: flex;
flex-direction: row;
padding: var(--digi--layout--gutter) var(--digi--layout--gutter);
background-color: var(--digi--ui--color--background--secondary);
}
.avrop-table-row__close-btn {
position: absolute;
top: 0;
right: 0;
::ng-deep {
button {
padding: 0.5rem 0.75rem !important;
}
}
}
.avrop-table-row__checkbox {
margin-top: 1.5rem;
}
.avrop-table-row__data-row {
display: flex;
flex-direction: row;
gap: $digi--layout--gutter--xl $digi--layout--gutter--l;
flex-grow: 1;
}
.avrop-table-row__data-column {
display: flex;
flex-direction: column;
gap: $digi--layout--gutter--l 0;
flex-grow: 1;
}
.avrop-table-row__data-column--bottom-align {
justify-content: flex-end;
}
.avrop-table__label {
display: block;
}

View File

@@ -1,30 +0,0 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { ButtonVariation } from '../../../enums/button-vatiation.enum';
import { HandledareAvrop } from '../../../models/handledare-avrop';
@Component({
selector: 'msfa-avrop-table-row',
templateUrl: './avrop-table-row.component.html',
styleUrls: ['./avrop-table-row.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropTableRowComponent {
@Input() deltagare: Avrop;
@Input() isSelected: boolean;
@Input() isLocked: boolean;
@Input() handledare: HandledareAvrop;
@Input() handledareConfirmed: boolean;
@Output() isSelectedChange = new EventEmitter<boolean>();
@Output() deleteDeltagareClicked = new EventEmitter<void>();
ButtonVariation = ButtonVariation;
emitSelectionChange(isSelected: boolean): void {
this.isSelectedChange.emit(isSelected);
}
emitDeltagareDeleted(): void {
this.deleteDeltagareClicked.emit();
}
}

View File

@@ -1,11 +0,0 @@
<div class="avrop-table">
<msfa-avrop-table-row
*ngFor="let deltagare of deltagareRows"
[deltagare]="deltagare"
[isSelected]="isSelected(deltagare)"
[isLocked]="isLocked"
(isSelectedChange)="isSelectedChange(deltagare, $event)"
[handledare]="handledare"
[handledareConfirmed]="handledareConfirmed"
></msfa-avrop-table-row>
</div>

View File

@@ -1,5 +0,0 @@
.avrop-table {
display: flex;
flex-direction: column;
gap: 1rem;
}

View File

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

View File

@@ -1,50 +0,0 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Avrop } from '@msfa-models/avrop.model';
import { BehaviorSubject, Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { HandledareAvrop } from '../../models/handledare-avrop';
@Component({
selector: 'msfa-avrop-table',
templateUrl: './avrop-table.component.html',
styleUrls: ['./avrop-table.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AvropTableComponent implements OnInit {
private _selectedDeltagare$ = new BehaviorSubject<Avrop[]>(null);
selectedDeltagareState$: Observable<Avrop[]> = this._selectedDeltagare$.asObservable();
@Input() selectableDeltagareList: Avrop[];
@Input() selectedDeltagareListInput: Avrop[];
@Input() handledare: HandledareAvrop;
@Input() isLocked: boolean;
@Input() handledareConfirmed: boolean;
@Output() changedSelectedDeltagareList = new EventEmitter<Avrop[]>();
get deltagareRows(): Avrop[] {
return this.isLocked ? this.selectedDeltagareListInput : this.selectableDeltagareList;
}
ngOnInit(): void {
this._selectedDeltagare$
.pipe(filter(x => !!x))
.subscribe(selectedDeltagare => this.changedSelectedDeltagareList.emit(selectedDeltagare));
// TODO lägg till unusubscribeOnDestroy
}
isSelected(deltagare: Avrop): boolean {
return this.selectedDeltagareListInput?.includes(deltagare) ?? false;
}
isSelectedChange(deltagare: Avrop, isSelected: boolean): void {
if (isSelected) {
return this._selectedDeltagare$.next([
...(this._selectedDeltagare$.value?.filter(deltagareInList => deltagareInList != deltagare) ?? []),
deltagare,
]);
}
return this._selectedDeltagare$.next(
this._selectedDeltagare$.value?.filter(deltagareInList => deltagareInList != deltagare) ?? []
);
}
}

View File

@@ -0,0 +1,11 @@
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { TemporaryFilterComponent } from './temporary-filter.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [TemporaryFilterComponent],
imports: [CommonModule],
exports: [TemporaryFilterComponent],
})
export class TemporaryFilterModule {}

View File

@@ -1,5 +0,0 @@
export enum ButtonSize {
S = 's',
M = 'm',
L = 'l',
}

View File

@@ -1,5 +0,0 @@
export enum ButtonVariation {
PRIMARY = 'primary',
SECONDARY = 'secondary',
TERTIARY = 'tertiary',
}

View File

@@ -1,4 +0,0 @@
export interface HandledareAvrop {
fullName: string;
id: string;
}

View File

@@ -37,9 +37,9 @@
</digi-table>
<digi-navigation-pagination
*ngIf="totalPages > 1"
*ngIf="totalPage > 1"
class="deltagare-list__pagination"
[afTotalPages]="totalPages"
[afTotalPages]="totalPage"
[afCurrentResultStart]="currentResultStart"
[afCurrentResultEnd]="currentResultEnd"
[afTotalResults]="count"

View File

@@ -35,8 +35,8 @@ export class DeltagareListComponent {
return this.paginationMeta.page;
}
get totalPages(): number {
return this.paginationMeta?.totalPages;
get totalPage(): number {
return this.paginationMeta?.totalPage;
}
get count(): number {