feat(export): Added export deltagare. (TV-872)
Squashed commit of the following: commit 9c06b7f1d44a4b48d7f31a22b6bfbd233e09d2f7 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 15:07:15 2021 +0100 Updated api endpoint commit f9bfbecda6d03b70d87febbada920d0eca798b1f Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 15:01:13 2021 +0100 Updated libs commit 9edb413d537288fa0708b8a04eb54f801ebc23a0 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:58:13 2021 +0100 Added @types/file-saver commit 624affac55ce771fd58e2770a0d4a98233a8e9ba Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:42:29 2021 +0100 Updated libs commit 65dae1d906bbcec474581692b2aced9e47d2484c Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:36:42 2021 +0100 Added utils lib config commit 223bd59724663523bdbaf87b5502396156ddb9eb Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:06:13 2021 +0100 Added validation commit 166dfcf0448155ac21c0eaa904b4ce1271f73193 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 13:25:35 2021 +0100 Changed styling and removed some fake data commit 3906f2793dd52b626b95c13e115495451332c894 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 13:18:52 2021 +0100 Added digi-ng datepicker commit de0d51434d15cac5476303d4b417c591da16fd8f Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 12:31:48 2021 +0100 Added checkbox
This commit is contained in:
22
angular.json
22
angular.json
@@ -244,6 +244,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"projectType": "library",
|
||||||
|
"root": "libs/utils",
|
||||||
|
"sourceRoot": "libs/utils/src",
|
||||||
|
"prefix": "mina-sidor-fa-web",
|
||||||
|
"architect": {
|
||||||
|
"lint": {
|
||||||
|
"builder": "@nrwl/linter:eslint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["libs/utils/src/**/*.ts", "libs/utils/src/**/*.html"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@nrwl/jest:jest",
|
||||||
|
"outputs": ["coverage/libs/utils"],
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "libs/utils/jest.config.js",
|
||||||
|
"passWithNoTests": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
Output,
|
Output,
|
||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
ViewChild
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
|
||||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
@@ -23,8 +23,8 @@ import { UtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model';
|
|||||||
import { UtforandeVerksamheterService } from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
|
import { UtforandeVerksamheterService } from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
|
||||||
import { ValidationErrorLink } from '@msfa-shared/components/error-list/error-list.component';
|
import { ValidationErrorLink } from '@msfa-shared/components/error-list/error-list.component';
|
||||||
import { TreeNodesSelectorService } from '@msfa-shared/components/tree-nodes-selector/services/tree-nodes-selector.service';
|
import { TreeNodesSelectorService } from '@msfa-shared/components/tree-nodes-selector/services/tree-nodes-selector.service';
|
||||||
import { uuid } from '@msfa-utils/uuid.util';
|
|
||||||
import { EmployeeValidator } from '@msfa-utils/validators/employee.validator';
|
import { EmployeeValidator } from '@msfa-utils/validators/employee.validator';
|
||||||
|
import { uuid } from '@utils/uuid.util';
|
||||||
import { EmployeeFormService } from '../services/employee-form.service';
|
import { EmployeeFormService } from '../services/employee-form.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
import { ExportsComponent } from './exports.component';
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', data: { title: 'Exporter' }, component: ExportsComponent },
|
{
|
||||||
|
path: '',
|
||||||
|
redirectTo: 'deltagare',
|
||||||
|
pathMatch: 'full',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'deltagare',
|
path: 'deltagare',
|
||||||
data: { title: 'Skapa export för deltagare' },
|
data: { title: 'Skapa export för deltagare' },
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
<digi-typography>
|
<digi-typography>
|
||||||
<header class="exports__header">
|
<header class="exports__header">
|
||||||
<h1>Exporter</h1>
|
<h1>Exporter</h1>
|
||||||
|
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ut corporis voluptatum magnam cumque fugiat, neque, totam explicabo est reprehenderit perspiciatis ratione error ad eveniet inventore, tempora facilis nostrum earum! Unde!</p>
|
||||||
</header>
|
</header>
|
||||||
<main class="exports__contents"></main>
|
<main class="exports__contents">
|
||||||
|
|
||||||
|
</main>
|
||||||
</digi-typography>
|
</digi-typography>
|
||||||
</section>
|
</section>
|
||||||
</msfa-layout>
|
</msfa-layout>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
|
import { ExportsRoutingModule } from './exports-routing.module';
|
||||||
import { ExportsComponent } from './exports.component';
|
import { ExportsComponent } from './exports.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [ExportsComponent],
|
declarations: [ExportsComponent],
|
||||||
imports: [CommonModule, RouterModule.forChild([{ path: '', component: ExportsComponent }]), LayoutModule],
|
imports: [CommonModule, ExportsRoutingModule, LayoutModule, ReactiveFormsModule],
|
||||||
})
|
})
|
||||||
export class ExportsModule {}
|
export class ExportsModule {}
|
||||||
|
|||||||
@@ -1,10 +1,62 @@
|
|||||||
<msfa-layout>
|
<msfa-layout>
|
||||||
<section class="deltagare-export">
|
<section class="deltagare-export">
|
||||||
<digi-typography>
|
<digi-typography>
|
||||||
<header class="deltagare-export__header">
|
<header class="deltagare-export__header">
|
||||||
<h1>Deltagare export</h1>
|
<h1>Exportera information om deltagare</h1>
|
||||||
|
<p>
|
||||||
|
Här kan du exportera deltagarinformation. Informationen laddas ner till din dator i CSV-format. Du kan endast
|
||||||
|
exportera deltagare som du har behörighet att se. Detta innebär att du bara kan få ut de deltagare som är
|
||||||
|
kopplade till samma utförande verksamhet och adress som du är inloggad på. Du kan även välja att inkludera
|
||||||
|
deltagare som exporterats vid tidigare tillfälle. Deltagare som exporterats tidigare markeras i filen.
|
||||||
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<main class="deltagare-export__contents"></main>
|
<form class="deltagare-export__form" [formGroup]="formGroup" (ngSubmit)="fetchExportFile()">
|
||||||
|
<div
|
||||||
|
class="deltagare-export__period-wrapper"
|
||||||
|
[ngClass]="{'deltagare-export__period-wrapper--error': showPeriodError}"
|
||||||
|
>
|
||||||
|
<div class="deltagare-export__period-inputs">
|
||||||
|
<digi-ng-form-datepicker
|
||||||
|
[afDisableValidStyle]="true"
|
||||||
|
[afMaxDate]="maxDate"
|
||||||
|
[afInvalid]="showPeriodError"
|
||||||
|
afLabel="Från datum"
|
||||||
|
[formControl]="startDateFormControl"
|
||||||
|
></digi-ng-form-datepicker>
|
||||||
|
<digi-ng-form-datepicker
|
||||||
|
[afDisableValidStyle]="true"
|
||||||
|
[afMaxDate]="maxDate"
|
||||||
|
[afInvalid]="showPeriodError"
|
||||||
|
afLabel="Till och med datum"
|
||||||
|
[formControl]="endDateFormControl"
|
||||||
|
></digi-ng-form-datepicker>
|
||||||
|
</div>
|
||||||
|
<div aria-atomic="true" role="alert">
|
||||||
|
<digi-form-validation-message *ngIf="showPeriodError" af-variation="error"
|
||||||
|
>{{formErrors.datesMismatch}}</digi-form-validation-message
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ui-checkbox
|
||||||
|
[formControl]="includeExportedDeltagareFormControl"
|
||||||
|
uiLabel="Inkludera redan exporterade deltagare"
|
||||||
|
></ui-checkbox>
|
||||||
|
<footer class="deltagare-export__footer">
|
||||||
|
<digi-notification-alert
|
||||||
|
*ngIf="fetchError$ | async as error"
|
||||||
|
class="deltagare-export__alert"
|
||||||
|
af-variation="danger"
|
||||||
|
af-heading="Någonting gick fel"
|
||||||
|
>
|
||||||
|
<p>Kunde inte hämta exportfilen för deltagare. Ladda om sidan och försök igen.</p>
|
||||||
|
<p class="msfa__small-text" *ngIf="error.message">{{error.message}}</p>
|
||||||
|
</digi-notification-alert>
|
||||||
|
<div class="deltagare-export__cta-wrapper">
|
||||||
|
<digi-button af-type="submit" af-size="m">Exportera deltagare</digi-button>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</form>
|
||||||
</digi-typography>
|
</digi-typography>
|
||||||
</section>
|
<ui-loader *ngIf="fetchIsLoading$ | async" uiType="absolute"></ui-loader>
|
||||||
|
</section>
|
||||||
</msfa-layout>
|
</msfa-layout>
|
||||||
|
|||||||
@@ -1,10 +1,26 @@
|
|||||||
@import 'variables/gutters';
|
@import 'variables/gutters';
|
||||||
|
@import 'variables/z-index';
|
||||||
|
|
||||||
.deltagare-export {
|
.deltagare-export {
|
||||||
&__contents {
|
max-width: var(--digi--typography--text--max-width);
|
||||||
|
position: relative;
|
||||||
|
z-index: $msfa__z-index-default;
|
||||||
|
|
||||||
|
&__form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: $digi--layout--gutter--l;
|
gap: $digi--layout--gutter--l;
|
||||||
margin-top: $digi--layout--gutter--l;
|
margin-top: $digi--layout--gutter--l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__period-inputs {
|
||||||
|
display: flex;
|
||||||
|
gap: $digi--layout--gutter--xl;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__footer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--digi--layout--gutter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { UiCheckboxModule } from '@ui/checkbox/checkbox.module';
|
||||||
import { DeltagareExportComponent } from './deltagare-export.component';
|
import { DeltagareExportComponent } from './deltagare-export.component';
|
||||||
|
import { DeltagareExportService } from './deltagare-export.service';
|
||||||
|
|
||||||
describe('DeltagareExportComponent', () => {
|
describe('DeltagareExportComponent', () => {
|
||||||
let component: DeltagareExportComponent;
|
let component: DeltagareExportComponent;
|
||||||
@@ -13,7 +17,15 @@ describe('DeltagareExportComponent', () => {
|
|||||||
void TestBed.configureTestingModule({
|
void TestBed.configureTestingModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [DeltagareExportComponent],
|
declarations: [DeltagareExportComponent],
|
||||||
imports: [HttpClientTestingModule, RouterTestingModule],
|
imports: [
|
||||||
|
HttpClientTestingModule,
|
||||||
|
RouterTestingModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
DigiNgFormDatepickerModule,
|
||||||
|
UiCheckboxModule,
|
||||||
|
],
|
||||||
|
providers: [DeltagareExportService],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { FormControl, FormGroup } from '@angular/forms';
|
||||||
|
import { DeltagareExportRequest } from '@msfa-models/api/deltagare-export.request.model';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
import { take } from 'rxjs/operators';
|
||||||
|
import { DeltagareExportFormData, DeltagareExportFormErrors } from './deltagare-export.model';
|
||||||
|
import { DeltagareExportService } from './deltagare-export.service';
|
||||||
|
import { DeltagareExportValidator } from './deltagare-export.validator';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-deltagare-export',
|
selector: 'msfa-deltagare-export',
|
||||||
@@ -6,4 +15,80 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
styleUrls: ['./deltagare-export.component.scss'],
|
styleUrls: ['./deltagare-export.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class DeltagareExportComponent {}
|
export class DeltagareExportComponent {
|
||||||
|
maxDate = new Date();
|
||||||
|
shouldValidate$ = new BehaviorSubject<boolean>(false);
|
||||||
|
fetchIsLoading$ = new BehaviorSubject<boolean>(false);
|
||||||
|
fetchError$ = new BehaviorSubject<CustomError>(null);
|
||||||
|
formGroup = new FormGroup(
|
||||||
|
{
|
||||||
|
startDate: new FormControl(null),
|
||||||
|
endDate: new FormControl(null),
|
||||||
|
includeExportedDeltagare: new FormControl(false),
|
||||||
|
},
|
||||||
|
[DeltagareExportValidator.isDeltagareExportValid()]
|
||||||
|
);
|
||||||
|
constructor(private deltagareExportService: DeltagareExportService) {}
|
||||||
|
|
||||||
|
get startDateFormControl(): FormControl {
|
||||||
|
return this.formGroup.get('startDate') as FormControl;
|
||||||
|
}
|
||||||
|
get endDateFormControl(): FormControl {
|
||||||
|
return this.formGroup.get('endDate') as FormControl;
|
||||||
|
}
|
||||||
|
get includeExportedDeltagareFormControl(): FormControl {
|
||||||
|
return this.formGroup.get('includeExportedDeltagare') as FormControl;
|
||||||
|
}
|
||||||
|
|
||||||
|
get deltagagareExportFormData(): DeltagareExportFormData {
|
||||||
|
return this.formGroup.value as DeltagareExportFormData;
|
||||||
|
}
|
||||||
|
|
||||||
|
get formErrors(): DeltagareExportFormErrors {
|
||||||
|
return this.formGroup.errors as DeltagareExportFormErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
get showPeriodError(): boolean {
|
||||||
|
return this.formErrors?.datesMismatch && this.shouldValidate$.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchExportFile(): void {
|
||||||
|
if (this.formGroup.invalid) {
|
||||||
|
this.shouldValidate$.next(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestData = this._formDataToRequestData(this.deltagagareExportFormData);
|
||||||
|
|
||||||
|
this.fetchIsLoading$.next(true);
|
||||||
|
this.deltagareExportService
|
||||||
|
.fetchExportFile$(requestData)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe({
|
||||||
|
next: response => {
|
||||||
|
const blob = new Blob([response], { type: 'application/csv' });
|
||||||
|
saveAs(blob, 'deltagare-export.csv');
|
||||||
|
this.fetchIsLoading$.next(false);
|
||||||
|
},
|
||||||
|
error: (customError: CustomError) => {
|
||||||
|
this.fetchError$.next({ ...customError, message: customError.error.message });
|
||||||
|
this.fetchIsLoading$.next(false);
|
||||||
|
throw { ...customError, avoidToast: true };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _formDataToRequestData(formData: DeltagareExportFormData): DeltagareExportRequest {
|
||||||
|
const { startDate, endDate, includeExportedDeltagare } = formData;
|
||||||
|
const requestData: DeltagareExportRequest = {
|
||||||
|
includeExportedDeltagare,
|
||||||
|
};
|
||||||
|
if (startDate) {
|
||||||
|
requestData.startDate = startDate;
|
||||||
|
}
|
||||||
|
if (endDate) {
|
||||||
|
requestData.endDate = endDate;
|
||||||
|
}
|
||||||
|
return requestData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export interface DeltagareExportFormData {
|
||||||
|
startDate: string;
|
||||||
|
endDate: string;
|
||||||
|
includeExportedDeltagare: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DeltagareExportFormKeys = keyof DeltagareExportFormData;
|
||||||
|
|
||||||
|
export interface DeltagareExportFormErrors {
|
||||||
|
datesMismatch?: string;
|
||||||
|
}
|
||||||
@@ -1,12 +1,27 @@
|
|||||||
|
import { DigiNgFormDatepickerModule } from '@af/digi-ng/_form/form-datepicker';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
|
import { UiCheckboxModule } from '@ui/checkbox/checkbox.module';
|
||||||
|
import { UiLoaderModule } from '@ui/loader/loader.module';
|
||||||
import { DeltagareExportComponent } from './deltagare-export.component';
|
import { DeltagareExportComponent } from './deltagare-export.component';
|
||||||
|
import { DeltagareExportService } from './deltagare-export.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [DeltagareExportComponent],
|
declarations: [DeltagareExportComponent],
|
||||||
imports: [CommonModule, RouterModule.forChild([{ path: '', component: DeltagareExportComponent }]), LayoutModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule.forChild([{ path: '', component: DeltagareExportComponent }]),
|
||||||
|
LayoutModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
UiCheckboxModule,
|
||||||
|
UiLoaderModule,
|
||||||
|
DigiNgFormDatepickerModule,
|
||||||
|
],
|
||||||
|
providers: [DeltagareExportService],
|
||||||
})
|
})
|
||||||
export class DeltagareExportModule {}
|
export class DeltagareExportModule {}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { DeltagareExportRequest } from '@msfa-models/api/deltagare-export.request.model';
|
||||||
|
import { ExportApiService } from '@msfa-services/api/export.api.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeltagareExportService {
|
||||||
|
constructor(private exportApiService: ExportApiService) {}
|
||||||
|
|
||||||
|
public fetchExportFile$(requestData: DeltagareExportRequest): Observable<Blob> {
|
||||||
|
return this.exportApiService.fetchDeltagareExportFile$(requestData);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { AbstractControl, ValidatorFn } from '@angular/forms';
|
||||||
|
import { DeltagareExportFormData, DeltagareExportFormErrors } from './deltagare-export.model';
|
||||||
|
|
||||||
|
export class DeltagareExportValidator {
|
||||||
|
static isDeltagareExportValid(): ValidatorFn {
|
||||||
|
return (c: AbstractControl): DeltagareExportFormErrors => {
|
||||||
|
let errors: DeltagareExportFormErrors = null;
|
||||||
|
const { startDate, endDate } = c.value as DeltagareExportFormData;
|
||||||
|
|
||||||
|
if (startDate && endDate && startDate > endDate) {
|
||||||
|
errors = {
|
||||||
|
...errors,
|
||||||
|
datesMismatch: 'Från datum får inte vara senare än till och med datum',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { DropdownTriggerForDirective } from '@msfa-shared/components/dropdown/dropdown-trigger-for.directive';
|
import { DropdownTriggerForDirective } from '@msfa-shared/components/dropdown/dropdown-trigger-for.directive';
|
||||||
import { MultiselectFilterOption } from '@msfa-shared/components/multiselect/multiselect-filter-option';
|
import { MultiselectFilterOption } from '@msfa-shared/components/multiselect/multiselect-filter-option';
|
||||||
import { uuid } from '@msfa-utils/uuid.util';
|
import { uuid } from '@utils/uuid.util';
|
||||||
|
|
||||||
interface PropagateChangeFn {
|
interface PropagateChangeFn {
|
||||||
(_: unknown): void;
|
(_: unknown): void;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
import { uuid } from '@msfa-utils/uuid.util';
|
import { uuid } from '@utils/uuid.util';
|
||||||
import { TreeNode, TreeNodeModel, TreeNodesSelectorService } from '../../services/tree-nodes-selector.service';
|
import { TreeNode, TreeNodeModel, TreeNodesSelectorService } from '../../services/tree-nodes-selector.service';
|
||||||
import { TreeNodesSelectorPanelComponent } from '../tree-nodes-selector-panel/tree-nodes-selector-panel.component';
|
import { TreeNodesSelectorPanelComponent } from '../tree-nodes-selector-panel/tree-nodes-selector-panel.component';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { uuid } from '@msfa-utils/uuid.util';
|
import { uuid } from '@utils/uuid.util';
|
||||||
|
|
||||||
export interface TreeNode {
|
export interface TreeNode {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export interface DeltagareExportRequest {
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
includeExportedDeltagare: boolean;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ErrorType } from '@msfa-enums/error-type.enum';
|
||||||
|
import { environment } from '@msfa-environment';
|
||||||
|
import { DeltagareExportRequest } from '@msfa-models/api/deltagare-export.request.model';
|
||||||
|
import { Params } from '@msfa-models/api/params.model';
|
||||||
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class ExportApiService {
|
||||||
|
private _apiBaseUrl = `${environment.api.url}/export`;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
|
public fetchDeltagareExportFile$(requestData: DeltagareExportRequest): Observable<Blob> {
|
||||||
|
const params: Params = {
|
||||||
|
...requestData,
|
||||||
|
includeExportedDeltagare: requestData.includeExportedDeltagare.toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.httpClient
|
||||||
|
.get<Blob>(`${this._apiBaseUrl}/deltagare`, { params, responseType: 'blob' as 'json' })
|
||||||
|
.pipe(
|
||||||
|
catchError((error: Error) => {
|
||||||
|
throw new CustomError({
|
||||||
|
error,
|
||||||
|
message: `Kunde inte hämta exportfilen för deltagare.\n\n${error.message}`,
|
||||||
|
type: ErrorType.API,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ export function mapPathsToBreadcrumbs(
|
|||||||
} else if (isDeltagareReportingRoute(paths)) {
|
} else if (isDeltagareReportingRoute(paths)) {
|
||||||
breadcrumbs = breadcrumbs.slice(0, -1);
|
breadcrumbs = breadcrumbs.slice(0, -1);
|
||||||
breadcrumbs[breadcrumbs.length - 2].text = 'Deltagarinformation';
|
breadcrumbs[breadcrumbs.length - 2].text = 'Deltagarinformation';
|
||||||
|
} else if (isExportRoute(paths)) {
|
||||||
|
breadcrumbs[breadcrumbs.length - 1].text = `Exportera ${paths[paths.length - 1]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return breadcrumbs;
|
return breadcrumbs;
|
||||||
@@ -54,3 +56,7 @@ function isDeltagareReportingFormRoute(paths: string[]): boolean {
|
|||||||
function isDeltagareReportingRoute(paths: string[]): boolean {
|
function isDeltagareReportingRoute(paths: string[]): boolean {
|
||||||
return paths.length === 4 && paths[0] === 'deltagare' && paths[2] in DELTAGARE_REPORTING_ROUTES;
|
return paths.length === 4 && paths[0] === 'deltagare' && paths[2] in DELTAGARE_REPORTING_ROUTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isExportRoute(paths: string[]): boolean {
|
||||||
|
return paths[0] === 'exporter';
|
||||||
|
}
|
||||||
|
|||||||
100
apps/mina-sidor-fa/src/assets/files/test.csv
Normal file
100
apps/mina-sidor-fa/src/assets/files/test.csv
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
Serial Number,Company Name,Employee Markme,Description,Leave
|
||||||
|
9788189999599,TALES OF SHIVA,Mark,mark,0
|
||||||
|
9780099578079,1Q84 THE COMPLETE TRILOGY,HARUKI MURAKAMI,Mark,0
|
||||||
|
9780198082897,MY KUMAN,Mark,Mark,0
|
||||||
|
9780007880331,THE GOD OF SMAAL THINGS,ARUNDHATI ROY,4TH HARPER COLLINS,2
|
||||||
|
9780545060455,THE BLACK CIRCLE,Mark,4TH HARPER COLLINS,0
|
||||||
|
9788126525072,THE THREE LAWS OF PERFORMANCE,Mark,4TH HARPER COLLINS,0
|
||||||
|
9789381626610,CHAMarkKYA MANTRA,Mark,4TH HARPER COLLINS,0
|
||||||
|
9788184513523,59.FLAGS,Mark,4TH HARPER COLLINS,0
|
||||||
|
9780743234801,THE POWER OF POSITIVE THINKING FROM,Mark,A & A PUBLISHER,0
|
||||||
|
9789381529621,YOU CAN IF YO THINK YO CAN,PEALE,A & A PUBLISHER,0
|
||||||
|
9788183223966,DONGRI SE DUBAI TAK (MPH),Mark,A & A PUBLISHER,0
|
||||||
|
9788187776005,MarkLANDA ADYTAN KOSH,Mark,AADISH BOOK DEPOT,0
|
||||||
|
9788187776013,MarkLANDA VISHAL SHABD SAGAR,-,AADISH BOOK DEPOT,1
|
||||||
|
8187776021,MarkLANDA CONCISE DICT(ENG TO HINDI),Mark,AADISH BOOK DEPOT,0
|
||||||
|
9789384716165,LIEUTEMarkMarkT GENERAL BHAGAT: A SAGA OF BRAVERY AND LEADERSHIP,Mark,AAM COMICS,2
|
||||||
|
9789384716233,LN. MarkIK SUNDER SINGH,N.A,AAN COMICS,0
|
||||||
|
9789384850319,I AM KRISHMark,DEEP TRIVEDI,AATMAN INNOVATIONS PVT LTD,1
|
||||||
|
9789384850357,DON'T TEACH ME TOLERANCE INDIA,DEEP TRIVEDI,AATMAN INNOVATIONS PVT LTD,0
|
||||||
|
9789384850364,MUJHE SAHISHNUTA MAT SIKHAO BHARAT,DEEP TRIVEDI,AATMAN INNOVATIONS PVT LTD,0
|
||||||
|
9789384850746,SECRETS OF DESTINY,DEEP TRIVEDI,AATMAN INNOVATIONS PVT LTD,1
|
||||||
|
9789384850753,BHAGYA KE RAHASYA (HINDI) SECRET OF DESTINY,DEEP TRIVEDI,AATMAN INNOVATIONS PVT LTD,1
|
||||||
|
9788192669038,MEIN MANN HOON,DEEP TRIVEDI,AATMAN INNOVATIONS PVT LTD,0
|
||||||
|
9789384850098,I AM THE MIND,DEEP TRIVEDI,AATMARAM & SONS,0
|
||||||
|
9780349121420,THE ART OF CHOOSING,SHEEMark IYENGAR,ABACUS,0
|
||||||
|
9780349123462,IN SPITE OF THE GODS,EDWARD LUCE,ABACUS,1
|
||||||
|
9788188440061,QUESTIONS & ANWERS ABOUT THE GREAT BIBLE,Mark,ABC PUBLISHERS DISTRIBUTORS,4
|
||||||
|
9789382088189,NIBANDH EVAM KAHANI LEKHAN { HINDI },Mark,ABHI BOOKS,1
|
||||||
|
9789332703759,INDIAN ECONOMY SINCE INDEPENDENCE 27TH /E,UMA KAPILA,ACADEMIC FOUNDATION,1
|
||||||
|
9788171888016,ECONOMIC DEVELOPMENT AND POLICY IN INDIA,UMA KAPILA,ACADEMIC FOUNDATION,1
|
||||||
|
9789332704343,INDIAN ECONOMY PERFORMANCE 18TH/E 2017-2018,UMA KAPILA,ACADEMIC FOUNDATION,2
|
||||||
|
9789332703735,INDIAN ECONOMIC DEVELOPMENTSINCE 1947 (NO RETURMarkBLE),UMA KAPILA,ACADEMIC FOUNDATION,1
|
||||||
|
9789383454143,PRELIMS SPECIAL READING COMPREHENSION PAPER II CSAT,MarkGENDRA PRATAP,ACCESS PUBLISHING INDIA PVT.LTD,0
|
||||||
|
9789383454204,THE CONSTITUTION OF INDIA 2ND / E,AR KHAN,ACCESS PUBLISHING INDIA PVT.LTD,10
|
||||||
|
9789386361011,"INDIAN HERITAGE ,ART & CULTURE",MADHUKAR,ACCESS PUBLISHING INDIA PVT.LTD,10
|
||||||
|
9789383454303,BHARAT KA SAMVIDHAN,AR KHAN,ACCESS PUBLISHING INDIA PVT.LTD,4
|
||||||
|
9789383454471,"ETHICS, INTEGRITY & APTITUDE ( 3RD/E)","P N ROY ,G SUBBA RAO",ACCESS PUBLISHING INDIA PVT.LTD,10
|
||||||
|
9789383454563,GENERAL STUDIES PAPER -- I (2016),Mark,ACCESS PUBLISHING INDIA PVT.LTD,0
|
||||||
|
9789383454570,GENERAL STUDIES PAPER - II (2016),Mark,ACCESS PUBLISHING INDIA PVT.LTD,0
|
||||||
|
9789383454693,INDIAN AND WORLD GEOGRAPHY 2E,D R KHULLAR,ACCESS PUBLISHING INDIA PVT.LTD,10
|
||||||
|
9789383454709,VASTUNISTHA PRASHN SANGRAHA: BHARAT KA ITIHAS,MEEMarkKSHI KANT,ACCESS PUBLISHING INDIA PVT.LTD,0
|
||||||
|
9789383454723,"PHYSICAL, HUMAN AND ECONOMIC GEOGRAPHY",D R KHULLAR,ACCESS PUBLISHING INDIA PVT.LTD,4
|
||||||
|
9789383454730,WORLD GEOGRAPHY,DR KHULLAR,ACCESS PUBLISHING INDIA PVT.LTD,5
|
||||||
|
9789383454822,INDIA: MAP ENTRIES IN GEOGRAPHY,MAJID HUSAIN,ACCESS PUBLISHING INDIA PVT.LTD,5
|
||||||
|
9789383454853,GOOD GOVERMarkNCE IN INDIA 2/ED.,G SUBBA RAO,ACCESS PUBLISHING INDIA PVT.LTD,1
|
||||||
|
9789383454884,KAMYABI KE SUTRA-CIVIL SEWA PARIKSHA AAP KI MUTTHI MEIN,ASHOK KUMAR,ACCESS PUBLISHING INDIA PVT.LTD,0
|
||||||
|
9789383454891,GENERAL SCIENCE PRELIRY EXAM,Mark,ACCESS PUBLISHING INDIA PVT.LTD,0
|
||||||
|
9781742860190,SUCCESS AND DYSLEXIA,SUCCESS AND DYSLEXIA,ACER PRESS,0
|
||||||
|
9781742860114,AN EXTRAORDIMarkRY SCHOOL,SARA JAMES,ACER PRESS,0
|
||||||
|
9781742861463,POWERFUL PRACTICES FOR READING IMPROVEMENT,GLASSWELL,ACER PRESS,0
|
||||||
|
9781742862859,EARLY CHILDHOOD PLAY MATTERS,SHOMark BASS,ACER PRESS,0
|
||||||
|
9781742863641,LEADING LEARNING AND TEACHING,STEPHEN DINHAM,ACER PRESS,0
|
||||||
|
9781742863658,READING AND LEARNING DIFFICULTIES,PETER WESTWOOD,ACER PRESS,0
|
||||||
|
9781742863665,NUMERACY AND LEARNING DIFFICULTIES,PETER WOODLAND],ACER PRESS,0
|
||||||
|
9781742863771,TEACHING AND LEARNING DIFFICULTIES,PETER WOODLAND,ACER PRESS,0
|
||||||
|
9781742861678,USING DATA TO IMPROVE LEARNING,ANTHONY SHADDOCK,ACER PRESS,0
|
||||||
|
9781742862484,PATHWAYS TO SCHOOL SYSTEM IMPROVEMENT,MICHAEL GAFFNEY,ACER PRESS,0
|
||||||
|
9781742860176,FOR THOSE WHO TEACH,PHIL RIDDEN,ACER PRESS,0
|
||||||
|
9781742860213,KEYS TO SCHOOL LEADERSHIP,PHIL RIDDEN & JOHN DE NOBILE,ACER PRESS,0
|
||||||
|
9781742860220,DIVERSE LITERACIES IN EARLY CHILDHOOD,LEONIE ARTHUR,ACER PRESS,0
|
||||||
|
9781742860237,CREATIVE ARTS IN THE LIVESOF YOUNG CHILDREN,ROBYN EWING,ACER PRESS,0
|
||||||
|
9781742860336,SOCIAL AND EMOTIOMarkL DEVELOPMENT,ROS LEYDEN AND ERIN SHALE,ACER PRESS,0
|
||||||
|
9781742860343,DISCUSSIONS IN SCIENCE,TIM SPROD,ACER PRESS,0
|
||||||
|
9781742860404,YOUNG CHILDREN LEARNING MATHEMATICS,ROBERT HUNTING,ACER PRESS,0
|
||||||
|
9781742860626,COACHING CHILDREN,KELLY SUMICH,ACER PRESS,1
|
||||||
|
9781742860923,TEACHING PHYSICAL EDUCATIOMarkL IN PRIMARY SCHOOL,JANET L CURRIE,ACER PRESS,0
|
||||||
|
9781742861111,ASSESSMENT AND REPORTING,PHIL RIDDEN AND SANDY,ACER PRESS,0
|
||||||
|
9781742861302,COLLABORATION IN LEARNING,MAL LEE AND LORRAE WARD,ACER PRESS,0
|
||||||
|
9780864315250,RE-IMAGINING EDUCATIMarkL LEADERSHIP,BRIAN J.CALDWELL,ACER PRESS,0
|
||||||
|
9780864317025,TOWARDS A MOVING SCHOOL,FLEMING & KLEINHENZ,ACER PRESS,0
|
||||||
|
9780864317230,DESINGNING A THINKING A CURRICULAM,SUSAN WILKS,ACER PRESS,0
|
||||||
|
9780864318961,LEADING A DIGITAL SCHOOL,MAL LEE AND MICHEAL GAFFNEY,ACER PRESS,0
|
||||||
|
9780864319043,NUMERACY,WESTWOOD,ACER PRESS,0
|
||||||
|
9780864319203,TEACHING ORAL LANGUAGE,JOHN MUNRO,ACER PRESS,0
|
||||||
|
9780864319449,SPELLING,WESTWOOD,ACER PRESS,0
|
||||||
|
9788189999803,STORIES OF SHIVA,Mark,ACK,0
|
||||||
|
9788189999988,JAMSET JI TATA: THE MAN WHO SAW TOMORROW,,ACK,0
|
||||||
|
9788184820355,HEROES FROM THE MAHABHARTA { 5-IN-1 },Mark,ACK,0
|
||||||
|
9788184820553,SURYA,,ACK,0
|
||||||
|
9788184820645,TALES OF THE MOTHER GODDESS,-,ACK,0
|
||||||
|
9788184820652,ADVENTURES OF KRISHMark,Mark,ACK,0
|
||||||
|
9788184822113,MAHATMA GANDHI,Mark,ACK,1
|
||||||
|
9788184822120,TALES FROM THE PANCHATANTRA 3-IN-1,-,ACK,0
|
||||||
|
9788184821482,YET MORE TALES FROM THE JATAKAS { 3-IN-1 },AMarkNT PAI,ACK,0
|
||||||
|
9788184825763,LEGENDARY RULERS OF INDIA,-,ACK,0
|
||||||
|
9788184825862,GREAT INDIAN CLASSIC,Mark,ACK,0
|
||||||
|
9788184823219,TULSIDAS ' RAMAYAMark,Mark,ACK,0
|
||||||
|
9788184820782,TALES OF HANUMAN,-,ACK,0
|
||||||
|
9788184820089,VALMIKI'S RAMAYAMark,A C K,ACK,1
|
||||||
|
9788184825213,THE BEST OF INIDAN WIT AND WISDOM,Mark,ACK,0
|
||||||
|
9788184820997,MORE TALES FROM THE PANCHTANTRA,AMarkNT PAL,ACK,0
|
||||||
|
9788184824018,THE GREAT MUGHALS {5-IN-1},AMarkNT.,ACK,0
|
||||||
|
9788184824049,FAMOUS SCIENTISTS,Mark,ACK,0
|
||||||
|
9788184825978,KOMarkRK,Mark,ACK,0
|
||||||
|
9788184826098,THE MUGHAL COURT,REEMark,ACK,0
|
||||||
|
9788184821536,MORE STORIES FROM THE JATAKAS,Mark,ACK,0
|
||||||
|
9788184821543,MORE TALES OF BIRBAL,-,ACK,0
|
||||||
|
9788184821550,TALES FROM THE JATAKAS,-,ACK,0
|
||||||
|
9788184821567,RAMarkS OF MEWAR,-,ACK,0
|
||||||
|
9788184821574,THE SONS OF THE PANDAVAS,-,ACK,0
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
projects: ['<rootDir>/apps/mina-sidor-fa', '<rootDir>/libs/ui', '<rootDir>/libs/styles'],
|
projects: ['<rootDir>/apps/mina-sidor-fa', '<rootDir>/libs/ui', '<rootDir>/libs/styles', '<rootDir>/libs/utils'],
|
||||||
};
|
};
|
||||||
|
|||||||
18
libs/ui/src/checkbox/checkbox.component.html
Normal file
18
libs/ui/src/checkbox/checkbox.component.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="ui-checkbox" [ngClass]="{'ui-checkbox--invalid': uiInvalid && uiValidationMessage}">
|
||||||
|
<digi-form-checkbox
|
||||||
|
[afId]="uiId"
|
||||||
|
[afIndeterminate]="uiIndeterminate"
|
||||||
|
[afName]="uiName"
|
||||||
|
[afRequired]="uiRequired"
|
||||||
|
[afValidation]="uiInvalid ? 'error' : 'neutral'"
|
||||||
|
[afVariation]="uiSecondary ? 'secondary' : 'primary'"
|
||||||
|
[afLabel]="labelText"
|
||||||
|
[afChecked]="currentValue"
|
||||||
|
(afOnChange)="checkForChange($event.detail.target.checked)"
|
||||||
|
></digi-form-checkbox>
|
||||||
|
<div aria-atomic="true" role="alert">
|
||||||
|
<digi-form-validation-message *ngIf="uiInvalid && uiValidationMessage" af-variation="error"
|
||||||
|
>{{uiValidationMessage}}</digi-form-validation-message
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
8
libs/ui/src/checkbox/checkbox.component.scss
Normal file
8
libs/ui/src/checkbox/checkbox.component.scss
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.ui-checkbox {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&--invalid {
|
||||||
|
gap: var(--digi--layout--gutter--xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
libs/ui/src/checkbox/checkbox.component.spec.ts
Normal file
23
libs/ui/src/checkbox/checkbox.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { FormCheckboxComponent } from './form-checkbox.component';
|
||||||
|
|
||||||
|
export class MockInjector {
|
||||||
|
get = jest.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: max-classes-per-file
|
||||||
|
export class MockChangeDetectorRef {
|
||||||
|
markForCheck = jest.fn();
|
||||||
|
detach = jest.fn();
|
||||||
|
detectChanges = jest.fn();
|
||||||
|
reattach = jest.fn();
|
||||||
|
checkNoChanges = jest.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('FormCheckboxComponent', () => {
|
||||||
|
let component: FormCheckboxComponent;
|
||||||
|
it('should create', () => {
|
||||||
|
component = new FormCheckboxComponent(new MockInjector(), new MockChangeDetectorRef());
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
16
libs/ui/src/checkbox/checkbox.component.stories.ts
Normal file
16
libs/ui/src/checkbox/checkbox.component.stories.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { CheckboxComponent } from './checkbox.component';
|
||||||
|
import { UiCheckboxModule } from './checkbox.module';
|
||||||
|
|
||||||
|
export default { title: 'Checkbox', component: CheckboxComponent };
|
||||||
|
|
||||||
|
const componentModule = {
|
||||||
|
moduleMetadata: {
|
||||||
|
imports: [ReactiveFormsModule, UiCheckboxModule],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const standard = () => ({
|
||||||
|
...componentModule,
|
||||||
|
template: '<ui-checkbox></ui-checkbox>',
|
||||||
|
});
|
||||||
111
libs/ui/src/checkbox/checkbox.component.ts
Normal file
111
libs/ui/src/checkbox/checkbox.component.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Injector,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
Output,
|
||||||
|
SimpleChanges,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||||
|
import { uuid } from '@utils/uuid.util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A checkbox input. Implemented with control value accessor
|
||||||
|
*
|
||||||
|
* ## Usage
|
||||||
|
* ``import {UiCheckboxModule} from '@ui/checkbox/checkbox.module';``
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'ui-checkbox',
|
||||||
|
templateUrl: './checkbox.component.html',
|
||||||
|
styleUrls: ['./checkbox.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: NG_VALUE_ACCESSOR,
|
||||||
|
useExisting: CheckboxComponent,
|
||||||
|
multi: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class CheckboxComponent implements AfterViewInit, ControlValueAccessor, OnChanges {
|
||||||
|
@Input() uiInvalid: boolean;
|
||||||
|
@Input() uiValidationMessage: string;
|
||||||
|
@Input() uiSecondary: boolean;
|
||||||
|
@Input() uiIndeterminate: boolean = false;
|
||||||
|
@Input() uiLabel: string = '';
|
||||||
|
@Input() uiRequired: boolean;
|
||||||
|
@Input() uiId: string = uuid();
|
||||||
|
@Input() uiName: string;
|
||||||
|
@Input() uiAnnounceIfOptional: boolean = false;
|
||||||
|
@Output() uiOnChange: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
name: string | number;
|
||||||
|
|
||||||
|
onTouched: () => {};
|
||||||
|
private onChange: (value: any) => {};
|
||||||
|
private _value: boolean;
|
||||||
|
|
||||||
|
constructor(private injector: Injector, private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
get currentValue(): boolean {
|
||||||
|
return this._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
get labelText(): string {
|
||||||
|
return `${this.uiLabel}${this._requiredText}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _requiredText() {
|
||||||
|
if (this.uiRequired && !this.uiAnnounceIfOptional) {
|
||||||
|
return ' (obligatoriskt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.uiRequired && this.uiAnnounceIfOptional) {
|
||||||
|
return ' (frivilligt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||||
|
if (ngControl) {
|
||||||
|
this.name = ngControl.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||||
|
if (ngControl) {
|
||||||
|
this.name = ngControl.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForChange(value: boolean): void {
|
||||||
|
if (this._value !== !!value) {
|
||||||
|
if (this.onChange) {
|
||||||
|
this.onChange(!!value);
|
||||||
|
}
|
||||||
|
this._value = !!value;
|
||||||
|
this.uiOnChange.emit(!!value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeValue(value: any): void {
|
||||||
|
this._value = value;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnChange(fn: (value: string) => {}) {
|
||||||
|
this.onChange = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerOnTouched(fn: () => {}) {
|
||||||
|
this.onTouched = fn;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
libs/ui/src/checkbox/checkbox.module.ts
Normal file
11
libs/ui/src/checkbox/checkbox.module.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { CheckboxComponent } from './checkbox.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
imports: [CommonModule],
|
||||||
|
declarations: [CheckboxComponent],
|
||||||
|
exports: [CheckboxComponent],
|
||||||
|
})
|
||||||
|
export class UiCheckboxModule {}
|
||||||
1
libs/ui/src/test-setup.ts
Normal file
1
libs/ui/src/test-setup.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import 'jest-preset-angular';
|
||||||
22
libs/utils/.eslintrc.json
Normal file
22
libs/utils/.eslintrc.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"extends": ["../../.eslintrc.json"],
|
||||||
|
"ignorePatterns": ["!**/*"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts"],
|
||||||
|
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
|
||||||
|
"parserOptions": { "project": ["libs/utils/tsconfig.*?.json"] },
|
||||||
|
"rules": {
|
||||||
|
"@angular-eslint/directive-selector": [
|
||||||
|
"error",
|
||||||
|
{ "type": "attribute", "prefix": "mina-sidor-fa-web", "style": "camelCase" }
|
||||||
|
],
|
||||||
|
"@angular-eslint/component-selector": [
|
||||||
|
"error",
|
||||||
|
{ "type": "element", "prefix": "mina-sidor-fa-web", "style": "kebab-case" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ "files": ["*.html"], "extends": ["plugin:@nrwl/nx/angular-template"], "rules": {} }
|
||||||
|
]
|
||||||
|
}
|
||||||
7
libs/utils/README.md
Normal file
7
libs/utils/README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# utils
|
||||||
|
|
||||||
|
This library was generated with [Nx](https://nx.dev).
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `nx test utils` to execute the unit tests.
|
||||||
23
libs/utils/jest.config.js
Normal file
23
libs/utils/jest.config.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
module.exports = {
|
||||||
|
displayName: 'utils',
|
||||||
|
preset: '../../jest.preset.js',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||||
|
globals: {
|
||||||
|
'ts-jest': {
|
||||||
|
tsConfig: '<rootDir>/tsconfig.spec.json',
|
||||||
|
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||||
|
astTransformers: {
|
||||||
|
before: [
|
||||||
|
'jest-preset-angular/build/InlineFilesTransformer',
|
||||||
|
'jest-preset-angular/build/StripStylesTransformer',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
coverageDirectory: '../../coverage/libs/utils',
|
||||||
|
snapshotSerializers: [
|
||||||
|
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
|
||||||
|
'jest-preset-angular/build/AngularSnapshotSerializer.js',
|
||||||
|
'jest-preset-angular/build/HTMLCommentSerializer.js',
|
||||||
|
],
|
||||||
|
};
|
||||||
1
libs/utils/src/test-setup.ts
Normal file
1
libs/utils/src/test-setup.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import 'jest-preset-angular';
|
||||||
13
libs/utils/tsconfig.json
Normal file
13
libs/utils/tsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"include": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.lib.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
19
libs/utils/tsconfig.lib.json
Normal file
19
libs/utils/tsconfig.lib.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"target": "es2015",
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": [],
|
||||||
|
"lib": ["dom", "es2018"]
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
|
"strictMetadataEmit": true,
|
||||||
|
"enableResourceInlining": true
|
||||||
|
},
|
||||||
|
"exclude": ["src/test-setup.ts", "**/*.spec.ts"],
|
||||||
|
"include": ["**/*.ts"]
|
||||||
|
}
|
||||||
10
libs/utils/tsconfig.spec.json
Normal file
10
libs/utils/tsconfig.spec.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"module": "commonjs",
|
||||||
|
"types": ["jest", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test-setup.ts"],
|
||||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
||||||
3
nx.json
3
nx.json
@@ -19,6 +19,7 @@
|
|||||||
"mina-sidor-fa": { "tags": [] },
|
"mina-sidor-fa": { "tags": [] },
|
||||||
"mina-sidor-fa-e2e": { "tags": [], "implicitDependencies": ["mina-sidor-fa"] },
|
"mina-sidor-fa-e2e": { "tags": [], "implicitDependencies": ["mina-sidor-fa"] },
|
||||||
"ui": { "tags": [] },
|
"ui": { "tags": [] },
|
||||||
"styles": { "tags": [] }
|
"styles": { "tags": [] },
|
||||||
|
"utils": { "tags": [] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1429
package-lock.json
generated
1429
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -63,6 +63,7 @@
|
|||||||
"@elastic/apm-rum-angular": "^2.1.1",
|
"@elastic/apm-rum-angular": "^2.1.1",
|
||||||
"@nrwl/angular": "11.5.1",
|
"@nrwl/angular": "11.5.1",
|
||||||
"date-fns": "^2.22.1",
|
"date-fns": "^2.22.1",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"ngx-markdown": "^11.1.3",
|
"ngx-markdown": "^11.1.3",
|
||||||
"rxjs": "~6.6.3",
|
"rxjs": "~6.6.3",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
@@ -89,6 +90,7 @@
|
|||||||
"@semantic-release/git": "^9.0.0",
|
"@semantic-release/git": "^9.0.0",
|
||||||
"@semantic-release/npm": "^7.1.3",
|
"@semantic-release/npm": "^7.1.3",
|
||||||
"@semantic-release/release-notes-generator": "^9.0.2",
|
"@semantic-release/release-notes-generator": "^9.0.2",
|
||||||
|
"@types/file-saver": "^2.0.3",
|
||||||
"@types/jest": "26.0.8",
|
"@types/jest": "26.0.8",
|
||||||
"@types/node": "12.12.38",
|
"@types/node": "12.12.38",
|
||||||
"@typescript-eslint/eslint-plugin": "4.3.0",
|
"@typescript-eslint/eslint-plugin": "4.3.0",
|
||||||
|
|||||||
@@ -30,8 +30,8 @@
|
|||||||
"@msfa-shared/*": ["apps/mina-sidor-fa/src/app/shared/*"],
|
"@msfa-shared/*": ["apps/mina-sidor-fa/src/app/shared/*"],
|
||||||
"@msfa-utils/*": ["apps/mina-sidor-fa/src/app/shared/utils/*"],
|
"@msfa-utils/*": ["apps/mina-sidor-fa/src/app/shared/utils/*"],
|
||||||
"@msfa-validators/*": ["apps/mina-sidor-fa/src/app/shared/utils/validators/*"],
|
"@msfa-validators/*": ["apps/mina-sidor-fa/src/app/shared/utils/validators/*"],
|
||||||
"@ui/*": ["libs/ui/*"],
|
"@ui/*": ["libs/ui/src/*"],
|
||||||
"@mina-sidor-fa-web/styles": ["libs/styles/src/index.ts"]
|
"@utils/*": ["libs/utils/src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
|
|||||||
Reference in New Issue
Block a user