Merge branch 'develop' of ssh://bitbucket.arbetsformedlingen.se:7999/tea/dafa-web-monorepo into develop
This commit is contained in:
@@ -1,56 +1,35 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { ExtraOptions, RouterModule, Routes } from '@angular/router';
|
import { ExtraOptions, RouterModule, Routes } from '@angular/router';
|
||||||
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { AuthGuard } from '@msfa-guards/auth.guard';
|
import { AuthGuard } from '@msfa-guards/auth.guard';
|
||||||
|
import { OrganizationGuard } from '@msfa-guards/organization.guard';
|
||||||
|
import { RoleGuard } from '@msfa-guards/role.guard';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
data: { title: '' },
|
data: { title: '' },
|
||||||
loadChildren: () => import('./pages/start/start.module').then(m => m.StartModule),
|
loadChildren: () => import('./pages/start/start.module').then(m => m.StartModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard, OrganizationGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'administration',
|
path: 'administration',
|
||||||
data: { title: 'Administration' },
|
data: { title: 'Administration', expectedRole: RoleEnum.MSFA_AuthAdmin },
|
||||||
loadChildren: () => import('./pages/administration/administration.module').then(m => m.AdministrationModule),
|
loadChildren: () => import('./pages/administration/administration.module').then(m => m.AdministrationModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard, OrganizationGuard, RoleGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'deltagare',
|
path: 'deltagare',
|
||||||
data: { title: 'Deltagare' },
|
data: { title: 'Deltagare', expectedRole: RoleEnum.MSFA_ReportAndPlanning },
|
||||||
loadChildren: () => import('./pages/deltagare/deltagare.module').then(m => m.DeltagareModule),
|
loadChildren: () => import('./pages/deltagare/deltagare.module').then(m => m.DeltagareModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard, OrganizationGuard, RoleGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'nya-deltagare',
|
path: 'nya-deltagare',
|
||||||
data: { title: 'Nya deltagare' },
|
data: { title: 'Nya deltagare', expectedRole: RoleEnum.MSFA_ReceiveDeltagare },
|
||||||
loadChildren: () => import('./pages/avrop/avrop.module').then(m => m.AvropModule),
|
loadChildren: () => import('./pages/avrop/avrop.module').then(m => m.AvropModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard, OrganizationGuard, RoleGuard],
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'meddelanden',
|
|
||||||
data: { title: 'Meddelanden' },
|
|
||||||
loadChildren: () => import('./pages/messages/messages.module').then(m => m.MessagesModule),
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'statistik',
|
|
||||||
data: { title: 'Statistik' },
|
|
||||||
loadChildren: () => import('./pages/statistics/statistics.module').then(m => m.StatisticsModule),
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'installningar',
|
|
||||||
data: { title: 'Inställningar' },
|
|
||||||
loadChildren: () => import('./pages/settings/settings.module').then(m => m.SettingsModule),
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'releases',
|
|
||||||
data: { title: 'Releaser' },
|
|
||||||
loadChildren: () => import('./pages/releases/releases.module').then(m => m.ReleasesModule),
|
|
||||||
canActivate: [AuthGuard],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'logga-ut',
|
path: 'logga-ut',
|
||||||
@@ -63,21 +42,36 @@ const routes: Routes = [
|
|||||||
data: { title: 'Välj organisation' },
|
data: { title: 'Välj organisation' },
|
||||||
loadChildren: () =>
|
loadChildren: () =>
|
||||||
import('./pages/organization-picker/organization-picker.module').then(m => m.OrganizationPickerModule),
|
import('./pages/organization-picker/organization-picker.module').then(m => m.OrganizationPickerModule),
|
||||||
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'mitt-konto',
|
path: 'mitt-konto',
|
||||||
data: { title: 'Mitt konto' },
|
data: { title: 'Mitt konto' },
|
||||||
loadChildren: () => import('./pages/my-account/my-account.module').then(m => m.MyAccountModule),
|
loadChildren: () => import('./pages/my-account/my-account.module').then(m => m.MyAccountModule),
|
||||||
|
canActivate: [AuthGuard, OrganizationGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'obehorig',
|
||||||
|
data: { title: 'Saknar behörighet' },
|
||||||
|
loadChildren: () => import('./pages/unauthorized/unauthorized.module').then(m => m.UnauthorizedModule),
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!environment.production) {
|
if (!environment.production) {
|
||||||
routes.push({
|
routes.push(
|
||||||
path: 'mock-login',
|
{
|
||||||
data: { title: 'Mock login' },
|
path: 'mock-login',
|
||||||
loadChildren: () => import('./pages/mock-login/mock-login.module').then(m => m.MockLoginModule),
|
data: { title: 'Mock login' },
|
||||||
});
|
loadChildren: () => import('./pages/mock-login/mock-login.module').then(m => m.MockLoginModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'releases',
|
||||||
|
data: { title: 'Releaser' },
|
||||||
|
loadChildren: () => import('./pages/releases/releases.module').then(m => m.ReleasesModule),
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
routes.push({
|
routes.push({
|
||||||
|
|||||||
@@ -17,11 +17,11 @@
|
|||||||
<a class="employee-card__edit-button" [routerLink]="['/administration/redigera-personalkonto', employee.id]"
|
<a class="employee-card__edit-button" [routerLink]="['/administration/redigera-personalkonto', employee.id]"
|
||||||
>Redigera</a
|
>Redigera</a
|
||||||
>
|
>
|
||||||
<h1>{{ employee.fullName }}</h1>
|
<h1>Personalkonto</h1>
|
||||||
</header>
|
</header>
|
||||||
<p>Här kan du se och ändra personalkontots behörigheter. Ändra behörighet genom att klicka på redigera.</p>
|
<p>Här ser ni personalkontot. Ändra behörighet genom att klicka på redigera.</p>
|
||||||
<div class="employee-card__contents">
|
<div class="employee-card__contents">
|
||||||
<div class="employee-card__column">
|
<div class="employee-card__block">
|
||||||
<h2>Personuppgifter</h2>
|
<h2>Personuppgifter</h2>
|
||||||
<dl class="employee-card__description-list">
|
<dl class="employee-card__description-list">
|
||||||
<dt>Förnamn</dt>
|
<dt>Förnamn</dt>
|
||||||
@@ -42,58 +42,73 @@
|
|||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div class="employee-card__column">
|
<div class="employee-card__block">
|
||||||
<h2>Tjänst</h2>
|
<h2>Behörigheter</h2>
|
||||||
|
<p>Här kan du se personalkontots behörigheter.</p>
|
||||||
|
</div>
|
||||||
|
<div class="employee-card__block">
|
||||||
|
<h3>Tjänst</h3>
|
||||||
<ul class="employee-card__list" *ngIf="employee.tjanster.length">
|
<ul class="employee-card__list" *ngIf="employee.tjanster.length">
|
||||||
<li *ngFor="let tjanst of employee.tjanster">{{ tjanst.name }}</li>
|
<li *ngFor="let tjanst of employee.tjanster">
|
||||||
|
<digi-icon-check-circle
|
||||||
|
class="employee-card__authorization-icon employee-card__authorization-icon--authorized"
|
||||||
|
></digi-icon-check-circle>
|
||||||
|
{{ tjanst.name }}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p *ngIf="!employee.tjanster.length">Kontot har inga registrerade tjänster ännu.</p>
|
<p *ngIf="!employee.tjanster.length">Kontot har inga registrerade tjänster ännu.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="employee-card__utforandeverksamheter">
|
<div class="employee-card__block">
|
||||||
<h2>Utförande verksamheter och utförande adresser</h2>
|
<h3>Utförande verksamheter och utförande adresser</h3>
|
||||||
<p *ngIf="employee.allaUtforandeVerksamheter; else specificUtforandeVerksamheter">
|
<p *ngIf="employee.allaUtforandeVerksamheter; else specificUtforandeVerksamheter">
|
||||||
Kontot har behörighet till alla utförande verksamheter och utförande adresser inom organisationen.
|
Kontot har behörighet till alla utförande verksamheter och utförande adresser inom organisationen.
|
||||||
</p>
|
</p>
|
||||||
<ng-template #specificUtforandeVerksamheter>
|
<ng-template #specificUtforandeVerksamheter>
|
||||||
<p style="color: red">
|
<ul
|
||||||
OBS: BEHÖVER FIXAS, ÄVEN OM MAN HAR UTFÖRANDE VERKSAMHETER SÅ SYNS DOM INTE DÅ VI BARA FÅR UT ID
|
|
||||||
</p>
|
|
||||||
<div
|
|
||||||
*ngIf="employee.utforandeVerksamheter?.length; else missingUtforandeVerksamheter"
|
*ngIf="employee.utforandeVerksamheter?.length; else missingUtforandeVerksamheter"
|
||||||
class="employee-card__utforandeverksamheter-cards"
|
class="employee-card__utforandeverksamheter"
|
||||||
>
|
>
|
||||||
<digi-info-card
|
<li *ngFor="let utforandeVerksamhet of employee.utforandeVerksamheter">
|
||||||
*ngFor="let utforandeverksamhet of employee.utforandeVerksamhet"
|
<digi-info-card
|
||||||
[afHeading]="utforandeverksamhet.namn"
|
[afHeading]="utforandeVerksamhet.name"
|
||||||
af-heading-level="h2"
|
af-heading-level="h4"
|
||||||
af-type="info"
|
af-type="info"
|
||||||
class="employee-card__utforandeverksamheter-card"
|
class="employee-card__utforandeverksamhet-card"
|
||||||
>
|
>
|
||||||
<digi-ng-layout-expansion-panel *ngIf="utforandeverksamhet.adresser.length > 0">
|
<p *ngIf="utforandeVerksamhet.allaAdresser">Alla adresser inom utförande verksamheten valda.</p>
|
||||||
<span data-slot-trigger>
|
|
||||||
<!-- vad refererar accordionExpanded till här?? Templaten bygger inte om det inte finns en definition av variabeln.. {{ accordionExpanded ? 'Dölj' : 'Visa' }} {{utforandeverksamhet.adresser.length}} -->
|
<digi-ng-layout-expansion-panel
|
||||||
{{utforandeverksamhet.adresser.length === 1 ? 'adress' : 'adresser'}}
|
*ngIf="!utforandeVerksamhet.allaAdresser && utforandeVerksamhet.adresser.length > 0"
|
||||||
</span>
|
[afExpanded]="isAccordionExpanded(utforandeVerksamhet.id)"
|
||||||
<ul class="employee-card__utforandeverksamheter-address-list">
|
(click)="toggleAccordionExpanded(utforandeVerksamhet.id)"
|
||||||
<li
|
>
|
||||||
class="employee-card__utforandeverksamheter-address-list-item"
|
<span data-slot-trigger>
|
||||||
*ngFor="let address of utforandeverksamhet.adresser"
|
{{ isAccordionExpanded(utforandeVerksamhet.id) ? 'Dölj' : 'Visa' }}
|
||||||
>
|
{{utforandeVerksamhet.adresser.length}} {{utforandeVerksamhet.adresser.length === 1 ? 'adress' :
|
||||||
<span>{{address.adressrad}}</span>
|
'adresser'}}
|
||||||
<span>{{address.postnummer}}</span>
|
</span>
|
||||||
<span>{{address.postort}}</span>
|
<ul class="employee-card__adresser">
|
||||||
</li>
|
<li *ngFor="let address of utforandeVerksamhet.adresser; let last = last">
|
||||||
</ul>
|
{{address.name}}{{last ? '' : ','}}
|
||||||
</digi-ng-layout-expansion-panel>
|
</li>
|
||||||
</digi-info-card>
|
</ul>
|
||||||
</div>
|
</digi-ng-layout-expansion-panel>
|
||||||
|
</digi-info-card>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<ng-template #missingUtforandeVerksamheter>
|
<ng-template #missingUtforandeVerksamheter>
|
||||||
<p>Kontot har inga registrerade utförande verksamheter eller utförande adresser ännu.</p>
|
<p>Kontot har inga registrerade utförande verksamheter eller utförande adresser ännu.</p>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</div>
|
</div>
|
||||||
<div class="employee-card__column">
|
<div class="employee-card__block">
|
||||||
<h2>Behörigheter</h2>
|
<h3>Roller</h3>
|
||||||
|
<p>
|
||||||
|
Här ser du användarens specifika roller i systemet. Tänk på att rollen i systemet är begränsad till de
|
||||||
|
utförande verksamheter och adresser som användaren hör till. Användaren kan därför endast utföra uppgifter
|
||||||
|
och se information inom den/de utförande adresser som tilldelats användaren.
|
||||||
|
<msfa-roles-dialog></msfa-roles-dialog>.
|
||||||
|
</p>
|
||||||
<ul class="employee-card__list">
|
<ul class="employee-card__list">
|
||||||
<li *ngFor="let role of allRoles">
|
<li *ngFor="let role of allRoles">
|
||||||
<digi-icon-check-circle
|
<digi-icon-check-circle
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
@import 'functions/rem';
|
||||||
@import 'variables/gutters';
|
@import 'variables/gutters';
|
||||||
@import 'variables/colors';
|
@import 'variables/colors';
|
||||||
@import 'mixins/buttons';
|
@import 'mixins/buttons';
|
||||||
@@ -10,39 +11,31 @@
|
|||||||
gap: $digi--layout--gutter--l $digi--layout--gutter--l;
|
gap: $digi--layout--gutter--l $digi--layout--gutter--l;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__column {
|
&__block {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: var(--digi--typography--text--max-width);
|
max-width: var(--digi--typography--text--max-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__utforandeverksamheter-cards {
|
&__utforandeverksamheter {
|
||||||
|
@include msfa__reset-list;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 1rem;
|
gap: $digi--layout--gutter;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__utforandeverksamheter-card {
|
&__utforandeverksamhet-card {
|
||||||
--digi-info-card--padding:
|
--digi-info-card--padding: 1.5rem 1rem;
|
||||||
var(--digi--layout--padding--20)
|
|
||||||
var(--digi--layout--padding--40)
|
::ng-deep .digi-info-card__heading {
|
||||||
var(--digi--layout--padding--40)
|
margin-top: 0;
|
||||||
var(--digi--layout--padding--40);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__utforandeverksamheter-address-list {
|
&__adresser {
|
||||||
@include msfa__reset-list;
|
@include msfa__reset-list;
|
||||||
padding-top: var(--digi--layout--padding--10);
|
padding-top: var(--digi--layout--padding--10);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__utforandeverksamheter-address-list-item span:not(:empty):not(:last-child):after {
|
|
||||||
content: ', ';
|
|
||||||
}
|
|
||||||
|
|
||||||
&__utforandeverksamheter {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header,
|
&__header,
|
||||||
&__footer {
|
&__footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -78,12 +71,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__edit-button {
|
&__edit-button {
|
||||||
@include msfa-button-template(
|
@include msfa__button('secondary');
|
||||||
$msfa-button--background--secondary,
|
|
||||||
$msfa-button--text--secondary,
|
|
||||||
$msfa-button--hover--secondary
|
|
||||||
);
|
|
||||||
width: var(--digi-button--width);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__authorization-icon {
|
&__authorization-icon {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export class EmployeeCardComponent implements OnDestroy {
|
|||||||
employee$: Observable<Employee> = this.employeeService.employee$;
|
employee$: Observable<Employee> = this.employeeService.employee$;
|
||||||
lastUpdatedEmployeeId$: Observable<string> = this.employeeService.lastUpdatedEmployeeId$;
|
lastUpdatedEmployeeId$: Observable<string> = this.employeeService.lastUpdatedEmployeeId$;
|
||||||
allRoles: Role[] = this.employeeService.allRoles;
|
allRoles: Role[] = this.employeeService.allRoles;
|
||||||
|
accordionsExpanded = [];
|
||||||
|
|
||||||
constructor(private activatedRoute: ActivatedRoute, private employeeService: EmployeeService) {
|
constructor(private activatedRoute: ActivatedRoute, private employeeService: EmployeeService) {
|
||||||
this.employeeService.setCurrentEmployeeId(this.employeeId);
|
this.employeeService.setCurrentEmployeeId(this.employeeId);
|
||||||
@@ -34,7 +35,19 @@ export class EmployeeCardComponent implements OnDestroy {
|
|||||||
return this._pendingSelectedParticipants$.getValue();
|
return this._pendingSelectedParticipants$.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isAccordionExpanded(id: number): boolean {
|
||||||
|
return this.accordionsExpanded.includes(id);
|
||||||
|
}
|
||||||
|
|
||||||
closeUpdatedNotificationAlert(): void {
|
closeUpdatedNotificationAlert(): void {
|
||||||
this.employeeService.resetLastUpdatedEmployeeId();
|
this.employeeService.resetLastUpdatedEmployeeId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleAccordionExpanded(currentId: number): void {
|
||||||
|
if (this.accordionsExpanded.includes(currentId)) {
|
||||||
|
this.accordionsExpanded = this.accordionsExpanded.filter(id => id !== currentId);
|
||||||
|
} else {
|
||||||
|
this.accordionsExpanded.push(currentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
|
|||||||
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 { RouterModule } from '@angular/router';
|
||||||
|
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||||
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
|
import { RolesDialogModule } from '@msfa-shared/components/roles-dialog/roles-dialog.module';
|
||||||
import { LocalDatePipeModule } from '@msfa-shared/pipes/local-date/local-date.module';
|
import { LocalDatePipeModule } from '@msfa-shared/pipes/local-date/local-date.module';
|
||||||
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
|
||||||
import { EmployeeCardComponent } from './employee-card.component';
|
import { EmployeeCardComponent } from './employee-card.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@@ -20,7 +21,8 @@ import { EmployeeCardComponent } from './employee-card.component';
|
|||||||
DigiNgLayoutExpansionPanelModule,
|
DigiNgLayoutExpansionPanelModule,
|
||||||
LocalDatePipeModule,
|
LocalDatePipeModule,
|
||||||
HideTextModule,
|
HideTextModule,
|
||||||
BackLinkModule
|
BackLinkModule,
|
||||||
|
RolesDialogModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EmployeeCardModule {}
|
export class EmployeeCardModule {}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
(ngSubmit)="onFormSubmitted()"
|
(ngSubmit)="onFormSubmitted()"
|
||||||
>
|
>
|
||||||
<digi-ng-form-input
|
<digi-ng-form-input
|
||||||
class="edit-employee-form__input"
|
|
||||||
afId="edit-employee-form-email"
|
afId="edit-employee-form-email"
|
||||||
afLabel="E-post adress"
|
afLabel="E-post adress"
|
||||||
afType="email"
|
afType="email"
|
||||||
@@ -16,94 +15,102 @@
|
|||||||
[afInvalid]="emailFormControl.invalid && emailFormControl.touched"
|
[afInvalid]="emailFormControl.invalid && emailFormControl.touched"
|
||||||
></digi-ng-form-input>
|
></digi-ng-form-input>
|
||||||
|
|
||||||
<fieldset>
|
<div class="edit-employee-form__contents">
|
||||||
<legend>Tjänster</legend>
|
<div class="edit-employee-form__block">
|
||||||
<p>Välj de tjänster du vill ge personalen tillgång till.</p>
|
<h2>Behörigheter</h2>
|
||||||
<digi-ng-form-select
|
<p>Här kan du ändra personalkontots behörigheter.</p>
|
||||||
[formControl]="tjansterFormControl"
|
</div>
|
||||||
afLabel="Välj tjänster"
|
|
||||||
[afPlaceholder]="'Välj tjänst'"
|
|
||||||
[afSelectItems]="selectableTjansterFormItems"
|
|
||||||
[afDisableValidStyle]="true"
|
|
||||||
[afInvalid]="tjansterFormControl.invalid && tjansterFormControl.touched"
|
|
||||||
(afOnChange)="toggleTjanst()"
|
|
||||||
></digi-ng-form-select>
|
|
||||||
<digi-form-validation-message
|
|
||||||
*ngIf="tjansterFormControl.invalid && tjansterFormControl.touched"
|
|
||||||
af-variation="error"
|
|
||||||
>
|
|
||||||
Du måste välja minst en tjänst
|
|
||||||
</digi-form-validation-message>
|
|
||||||
<!-- Vi får se till att bygga en kontrol för att kunna välja flera tjänster här istället, en digi-ng-select får vara en temporär lösning.. -->
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
<div class="edit-employee-form__block">
|
||||||
<legend>Utförande verksamheter och adresser</legend>
|
<h3>Tjänster</h3>
|
||||||
<p>Välj de utförandeverksamheter och utförande adresser du vill ge personalen behörighet till.</p>
|
<p>Välj de tjänster du vill ge personalen tillgång till.</p>
|
||||||
<p *ngIf="!availableUtforandeVerksamheter || availableUtforandeVerksamheter.length === 0">
|
<!-- Vi får se till att bygga en kontrol för att kunna välja flera tjänster här istället, en digi-ng-select får vara en temporär lösning.. -->
|
||||||
<strong>Du måste välja en eller flera tjänster för att kunna välja utförande verksamheter.</strong>
|
<digi-ng-form-select
|
||||||
</p>
|
[formControl]="tjansterFormControl"
|
||||||
<div class="edit-employee-form__choose_all-utforande-verksamh">
|
afLabel="Välj tjänster"
|
||||||
|
[afPlaceholder]="'Välj tjänst'"
|
||||||
|
[afSelectItems]="selectableTjansterFormItems"
|
||||||
|
[afDisableValidStyle]="true"
|
||||||
|
[afInvalid]="tjansterFormControl.invalid && tjansterFormControl.touched"
|
||||||
|
(afOnChange)="toggleTjanst()"
|
||||||
|
></digi-ng-form-select>
|
||||||
|
<digi-form-validation-message
|
||||||
|
*ngIf="tjansterFormControl.invalid && tjansterFormControl.touched"
|
||||||
|
af-variation="error"
|
||||||
|
>
|
||||||
|
Du måste välja minst en tjänst
|
||||||
|
</digi-form-validation-message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="edit-employee-form__block">
|
||||||
|
<h3>Utförande verksamheter och adresser</h3>
|
||||||
|
<p>Välj de utförandeverksamheter och utförande adresser du vill ge personalen behörighet till.</p>
|
||||||
|
<p *ngIf="!availableUtforandeVerksamheter || availableUtforandeVerksamheter.length === 0">
|
||||||
|
<strong>Du måste välja en eller flera tjänster för att kunna välja utförande verksamheter.</strong>
|
||||||
|
</p>
|
||||||
<digi-ng-form-checkbox
|
<digi-ng-form-checkbox
|
||||||
|
class="edit-employee-form__choose-all-utforande-verksamheter"
|
||||||
[formControl]="toggleAllUtforandeVerksamhetFormControl"
|
[formControl]="toggleAllUtforandeVerksamhetFormControl"
|
||||||
[afLabel]="'Välj alla utförande verksamheter och alla utförande adresser'"
|
[afLabel]="'Välj alla utförande verksamheter och alla utförande adresser'"
|
||||||
(afOnChange)="toggleAllUtforandeVerksamheter($event)"
|
(afOnChange)="toggleAllUtforandeVerksamheter($event)"
|
||||||
>
|
></digi-ng-form-checkbox>
|
||||||
</digi-ng-form-checkbox>
|
|
||||||
|
<msfa-tree-nodes-selector
|
||||||
|
*ngIf="!toggleAllUtforandeVerksamhetFormControl.value"
|
||||||
|
[headingText]="'Välj utförande verksamheter och adresser'"
|
||||||
|
[formControlName]="utforandeVerksamhetFormControlName"
|
||||||
|
[isInvalid]="utforandeVerksamhetFormControl?.invalid"
|
||||||
|
[showValidation]="utforandeVerksamhetFormControl?.touched"
|
||||||
|
[validationMessages]="utforandeVerksamhetFormControl.errors?.required ? ['Välj minst en utförande verksamhet'] : []"
|
||||||
|
(selectedTreeNodesChanged)="updateToggleAllUtforandeVerksamheter()"
|
||||||
|
></msfa-tree-nodes-selector>
|
||||||
</div>
|
</div>
|
||||||
<msfa-tree-nodes-selector
|
|
||||||
*ngIf="!toggleAllUtforandeVerksamhetFormControl.value"
|
<div class="edit-employee-form__block" *ngIf="rolesFormGroup && availableRoles" [formGroup]="rolesFormGroup">
|
||||||
[headingText]="'Välj utförande verksamheter och adresser'"
|
<h3>Roller</h3>
|
||||||
[formControlName]="utforandeVerksamhetFormControlName"
|
<p>
|
||||||
[isInvalid]="utforandeVerksamhetFormControl?.invalid"
|
Här tilldelar du specifika roller i systemet. Välj nedan vilka arbetsuppgifter som användaren ska kunna
|
||||||
[showValidation]="utforandeVerksamhetFormControl?.touched"
|
utföra. Tänk på att rollen i systemet är begränsad till de utförande verksamheter och adresser som användaren
|
||||||
[validationMessages]="utforandeVerksamhetFormControl.errors?.required ? ['Välj minst en utförande verksamhet'] : []"
|
hör till. Användaren kan därför endast utföra uppgifter och se information inom den/de utförande adresser som
|
||||||
(selectedTreeNodesChanged)="updateToggleAllUtforandeVerksamheter()"
|
tilldelats användaren. <msfa-roles-dialog></msfa-roles-dialog>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<fieldset class="edit-employee-form__fieldset">
|
||||||
|
<legend class="msfa__a11y-sr-only">Välj roller</legend>
|
||||||
|
<ul class="edit-employee-form__roles">
|
||||||
|
<li class="edit-employee-form__role" *ngFor="let role of availableRoles">
|
||||||
|
<digi-ng-form-checkbox
|
||||||
|
[afLabel]="role.name"
|
||||||
|
[formControlName]="getFormControlName(role)"
|
||||||
|
></digi-ng-form-checkbox>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<digi-notification-alert
|
||||||
|
*ngIf="errorWhileUpdating"
|
||||||
|
af-variation="danger"
|
||||||
|
af-heading="Kunde inte spara"
|
||||||
|
af-heading-level="h4"
|
||||||
|
[afCloseable]="true"
|
||||||
|
(afOnClose)="emitCloseError()"
|
||||||
>
|
>
|
||||||
</msfa-tree-nodes-selector>
|
<p>Personalkontot för {{employee.fullName}} kunde inte redigeras. Vänligen försök igen.</p>
|
||||||
</fieldset>
|
<p class="msfa__small-text" *ngIf="errorWhileUpdating.message">{{errorWhileUpdating.message}}</p>
|
||||||
|
</digi-notification-alert>
|
||||||
|
|
||||||
<fieldset *ngIf="rolesFormGroup && availableRoles" [formGroup]="rolesFormGroup">
|
<digi-notification-alert
|
||||||
<legend>Behörigheter</legend>
|
*ngIf="displayPristineWarning"
|
||||||
<p>
|
af-variation="warning"
|
||||||
Här tilldelar du specifika behörigheter i systemet. Välj nedan vilka arbetsuppgifter som användaren ska kunna
|
af-heading="Du har inte gjort några ändringar"
|
||||||
utföra. Tänk på att behörigheten i systemet är begränsad till de utförande verksamheter och adresser som
|
af-heading-level="h4"
|
||||||
användaren hör till. Användaren kan därför endast utföra uppgifter och se information inom den/ de utförande
|
[afCloseable]="true"
|
||||||
adresser som tilldelats användaren.
|
(afOnClose)="displayPristineWarning = false"
|
||||||
<digi-ng-button
|
>
|
||||||
class="edit-employee-form__open-roles-dialog-btn"
|
<p>Du har inte gjort några ändringar i formuläret. För att spara personalkontot behöver ändringar göras.</p>
|
||||||
[afVariation]="ButtonVariation.TERTIARY"
|
</digi-notification-alert>
|
||||||
[afType]="ButtonType.BUTTON"
|
</div>
|
||||||
[afSize]="ButtonSize.S"
|
|
||||||
afAriaControls="roles-dialog"
|
|
||||||
[afAriaLabel]="'Öppnar en dialog med information om behörigheter'"
|
|
||||||
(afOnClick)="openRolesDialog()"
|
|
||||||
>
|
|
||||||
Läs mer om behörigheter här
|
|
||||||
</digi-ng-button>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<ul class="edit-employee-form__roles">
|
|
||||||
<li class="edit-employee-form__roles-item" *ngFor="let role of availableRoles">
|
|
||||||
<digi-ng-form-checkbox
|
|
||||||
[afLabel]="role.name"
|
|
||||||
[formControlName]="getFormControlName(role)"
|
|
||||||
></digi-ng-form-checkbox>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<digi-notification-alert
|
|
||||||
*ngIf="errorWhileUpdating"
|
|
||||||
af-variation="danger"
|
|
||||||
af-heading="Kunde inte spara"
|
|
||||||
af-heading-level="h2"
|
|
||||||
[afCloseable]="true"
|
|
||||||
(afOnClose)="emitCloseError()"
|
|
||||||
>
|
|
||||||
<p>Personalkontot för {{employee.fullName}} kunde inte redigeras. Vänligen försök igen.</p>
|
|
||||||
<p class="msfa__small-text" *ngIf="errorWhileUpdating.message">{{errorWhileUpdating.message}}</p>
|
|
||||||
</digi-notification-alert>
|
|
||||||
|
|
||||||
<footer class="edit-employee-form__footer">
|
<footer class="edit-employee-form__footer">
|
||||||
<a
|
<a
|
||||||
@@ -122,7 +129,7 @@
|
|||||||
(afOnPrimaryClick)="onFormSubmitted(true)"
|
(afOnPrimaryClick)="onFormSubmitted(true)"
|
||||||
(afOnSecondaryClick)="abortFormSubmit()"
|
(afOnSecondaryClick)="abortFormSubmit()"
|
||||||
(afOnInactive)="abortFormSubmit()"
|
(afOnInactive)="abortFormSubmit()"
|
||||||
afHeading="Är du säker"
|
afHeading="Är du säker?"
|
||||||
afHeadingLevel="h2"
|
afHeadingLevel="h2"
|
||||||
afPrimaryButtonText="Ja, spara ändå"
|
afPrimaryButtonText="Ja, spara ändå"
|
||||||
afSecondaryButtonText="Nej, gå tillbaka"
|
afSecondaryButtonText="Nej, gå tillbaka"
|
||||||
@@ -138,29 +145,29 @@
|
|||||||
[afActive]="displayRolesDialog"
|
[afActive]="displayRolesDialog"
|
||||||
(afOnInactive)="closeRolesDialog()"
|
(afOnInactive)="closeRolesDialog()"
|
||||||
(afOnPrimaryClick)="closeRolesDialog()"
|
(afOnPrimaryClick)="closeRolesDialog()"
|
||||||
afHeading="Om behörigheterna i systemet"
|
afHeading="Om rollerna i systemet"
|
||||||
afHeadingLevel="h2"
|
afHeadingLevel="h2"
|
||||||
afPrimaryButtonText="Stäng"
|
afPrimaryButtonText="Stäng"
|
||||||
afSecondaryButtonText=""
|
afSecondaryButtonText=""
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
Läs beskrivningarna nedan för att lära dig mer om de olika behörigheterna. Personalen kan tilldelas en behörighet,
|
Läs beskrivningarna nedan för att lära dig mer om de olika rollerna. Personalen kan tilldelas en roll, eller flera
|
||||||
eller flera behörigheter, beroende på vad de arbetar med. Tänk på att behörigheten endast gäller inom de utförande
|
roller, beroende på vad de arbetar med. Tänk på att rollen endast gäller inom de utförande verksamheter och adresser
|
||||||
verksamheter och adresser som personalen fått behörighet till.
|
som personalen fått behörighet till.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
All personal kommer att kunna se sitt eget personalkonto, där de kan se vilka behörigheter och utförande
|
All personal kommer att kunna se sitt eget personalkonto, där de kan se vilka roller och utförande verksamheter och
|
||||||
verksamheter och adresser som tilldelats dem i systemet. De kommer även att se startsidan.
|
adresser som tilldelats dem i systemet. De kommer även att se startsidan.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Administrera behörigheter</h3>
|
<h3>Administrera behörigheter</h3>
|
||||||
<p>
|
<p>
|
||||||
Behörigheten passar personal som ska administrera behörigheter i systemet. Behörigheten bör begränsas till ett fåtal
|
Rollen passar personal som ska administrera behörigheter i systemet. Rollen bör begränsas till ett fåtal personer
|
||||||
personer och kan användas av exempelvis firmatecknare, behörighetsadministratör, eller annan person som ska kunna
|
och kan användas av exempelvis firmatecknare, behörighetsadministratör, eller annan person som ska kunna
|
||||||
administrera personalens behörigheter. Behörigheten gäller endast inom de utförande verksamheter och adresser som
|
administrera personalens behörigheter. Rollen gäller endast inom de utförande verksamheter och adresser som getts
|
||||||
getts behörighet till.
|
behörighet till.
|
||||||
</p>
|
</p>
|
||||||
<p>Behörigheten ger tillgång till följande funktioner:</p>
|
<p>Rollen ger tillgång till följande funktioner:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Skapa nya personalkonton</li>
|
<li>Skapa nya personalkonton</li>
|
||||||
<li>Se personallista</li>
|
<li>Se personallista</li>
|
||||||
@@ -170,12 +177,12 @@
|
|||||||
|
|
||||||
<h3>Ta emot nya deltagare</h3>
|
<h3>Ta emot nya deltagare</h3>
|
||||||
<p>
|
<p>
|
||||||
Behörigheten passar personal som ska se nya deltagare som inkommit i systemet och som ska tilldela handledare till
|
Rollen passar personal som ska se nya deltagare som inkommit i systemet och som ska tilldela handledare till nya
|
||||||
nya deltagare. Behörigheten kan exempelvis användas av samordnande roller, handledare, administratörer, eller annan
|
deltagare. Rollen kan exempelvis användas av samordnande roller, handledare, administratörer, eller annan personal
|
||||||
personal som ska kunna utföra dessa arbetsuppgifter. Behörigheten gäller endast inom de utförande verksamheter och
|
som ska kunna utföra dessa arbetsuppgifter. Rollen gäller endast inom de utförande verksamheter och adresser som
|
||||||
adresser som getts behörighet till.
|
getts behörighet till.
|
||||||
</p>
|
</p>
|
||||||
<p>Behörigheten ger tillgång till följande funktioner:</p>
|
<p>Rollen ger tillgång till följande funktioner:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Se lista över nya deltagare som inkommit</li>
|
<li>Se lista över nya deltagare som inkommit</li>
|
||||||
<li>Tilldela handledare till nya deltagare</li>
|
<li>Tilldela handledare till nya deltagare</li>
|
||||||
@@ -184,12 +191,12 @@
|
|||||||
|
|
||||||
<h3>Rapportering, planering och information om deltagare</h3>
|
<h3>Rapportering, planering och information om deltagare</h3>
|
||||||
<p>
|
<p>
|
||||||
Behörigheten passar personal som arbetar nära deltagare. Behörigheten kan användas av exempelvis handledare,
|
Rollen passar personal som arbetar nära deltagare. Rollen kan användas av exempelvis handledare, coacher, studie-
|
||||||
coacher, studie- och yrkesvägledare, lärare eller andra roller som behöver se information om deltagare, planera
|
och yrkesvägledare, lärare eller andra roller som behöver se information om deltagare, planera aktiviteter med
|
||||||
aktiviteter med deltagare eller hantera deltagares rapporter. Behörigheten gäller endast inom de utförande
|
deltagare eller hantera deltagares rapporter. Rollen gäller endast inom de utförande verksamheter och adresser som
|
||||||
verksamheter och adresser som getts behörighet till.
|
getts behörighet till.
|
||||||
</p>
|
</p>
|
||||||
<p>Behörigheten ger tillgång till följande funktioner:</p>
|
<p>Rollen ger tillgång till följande funktioner:</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Se lista över deltagare</li>
|
<li>Se lista över deltagare</li>
|
||||||
|
|||||||
@@ -1,38 +1,24 @@
|
|||||||
|
@import 'mixins/buttons';
|
||||||
@import 'mixins/list';
|
@import 'mixins/list';
|
||||||
@import 'variables/gutters';
|
@import 'variables/gutters';
|
||||||
@import '~@digi/core/dist/collection/components/_button/button/button.css';
|
@import '~@digi/core/dist/collection/components/_button/button/button.css';
|
||||||
|
|
||||||
.edit-employee-form {
|
.edit-employee-form {
|
||||||
max-width: var(--digi--typography--text--max-width);
|
&__contents {
|
||||||
|
|
||||||
fieldset {
|
|
||||||
margin: 2.5rem 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0 none transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
legend {
|
|
||||||
width: 100%;
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
font-weight: var(--digi--typography--font-weight--semibold);
|
gap: $digi--layout--gutter--l $digi--layout--gutter--l;
|
||||||
font-size: var(--digi--typography--font-size--h2--desktop);
|
|
||||||
margin-bottom: var(--digi-typography--margin--h2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__service-tag {
|
&__block {
|
||||||
padding-top: .5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__service-tag--item {
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: var(--digi--layout--gutter);
|
max-width: var(--digi--typography--text--max-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__fieldset {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__roles {
|
&__roles {
|
||||||
@@ -40,7 +26,7 @@
|
|||||||
margin-bottom: var(--digi--layout--gutter);
|
margin-bottom: var(--digi--layout--gutter);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__roles-item {
|
&__role {
|
||||||
margin-top: var(--digi--layout--gutter);
|
margin-top: var(--digi--layout--gutter);
|
||||||
|
|
||||||
::ng-deep label {
|
::ng-deep label {
|
||||||
@@ -48,7 +34,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__open-roles-dialog-btn {
|
&__roles-dialog-button {
|
||||||
::ng-deep button {
|
::ng-deep button {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
@@ -61,40 +47,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__link-btn {
|
&__link-btn {
|
||||||
font-family: var(--digi-button--font-family);
|
@include msfa__button('secondary');
|
||||||
background: var(--digi-button--background);
|
|
||||||
color: var(--digi-button--color);
|
|
||||||
padding: var(--digi-button--padding);
|
|
||||||
border-radius: var(--digi-button--border-radius);
|
|
||||||
border: var(--digi-button--border);
|
|
||||||
border-color: var(--digi-button--border-color);
|
|
||||||
font-weight: var(--digi-button--font-weight);
|
|
||||||
font-size: var(--digi-button--font-size);
|
|
||||||
outline: var(--digi-button--outline);
|
|
||||||
text-align: var(--digi-button--text-align);
|
|
||||||
cursor: pointer;
|
|
||||||
width: var(--digi-button--width);
|
|
||||||
display: var(--digi-button--display);
|
|
||||||
text-align: var(--digi-button--text-align);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link-btn--secondary {
|
&__choose-all-utforande-verksamheter {
|
||||||
--digi-button--background: var(--digi-button--background--secondary);
|
display: block;
|
||||||
--digi-button--background--hover: var(--digi-button--background--secondary--hover);
|
|
||||||
--digi-button--color: var(--digi-button--color--secondary);
|
|
||||||
--digi-button--color--hover: var(--digi-button--color--secondary);
|
|
||||||
--digi-button--border-color--disabled: var(--digi-button--border-color--secondary--disabled);
|
|
||||||
|
|
||||||
&:focus,
|
|
||||||
&:hover {
|
|
||||||
--digi-button--background: var(--digi-button--background--hover);
|
|
||||||
--digi-button--border-color: var(--digi-button--border-color--hover);
|
|
||||||
--digi-button--color: var(--digi-button--color--hover);
|
|
||||||
--digi-button--outline: var(--digi-button--outline--focus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&__choose_all-utforande-verksamh {
|
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ describe('EditEmployeeFormComponent', () => {
|
|||||||
tjanster: [],
|
tjanster: [],
|
||||||
allaUtforandeVerksamheter: false,
|
allaUtforandeVerksamheter: false,
|
||||||
utforandeVerksamheter: [],
|
utforandeVerksamheter: [],
|
||||||
|
|
||||||
tjanstCodes: [],
|
|
||||||
utforandeVerksamhetIds: [],
|
|
||||||
utforandeAdressIds: [],
|
|
||||||
};
|
};
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ import { Employee } from '@msfa-models/employee.model';
|
|||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { Role } from '@msfa-models/role.model';
|
import { Role } from '@msfa-models/role.model';
|
||||||
import { Tjanst } from '@msfa-models/tjanst.model';
|
import { Tjanst } from '@msfa-models/tjanst.model';
|
||||||
import {
|
import { UtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model';
|
||||||
UtforandeVerksamhet,
|
import { UtforandeVerksamheterService } from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
|
||||||
UtforandeVerksamheterService,
|
|
||||||
} from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
|
|
||||||
import {
|
import {
|
||||||
TreeNode,
|
TreeNode,
|
||||||
TreeNodesSelectorService,
|
TreeNodesSelectorService,
|
||||||
@@ -30,13 +28,6 @@ import { RequiredValidator } from '@msfa-utils/validators/required.validator';
|
|||||||
import { TreeNodeValidator } from '@msfa-utils/validators/tree-node.validator';
|
import { TreeNodeValidator } from '@msfa-utils/validators/tree-node.validator';
|
||||||
import { EmployeeFormService } from '../services/employee-form.service';
|
import { EmployeeFormService } from '../services/employee-form.service';
|
||||||
|
|
||||||
export interface EditEmployeeFormData {
|
|
||||||
email: string;
|
|
||||||
tjanster: Tjanst[];
|
|
||||||
roles: Role[];
|
|
||||||
utforandeVerksamheter: UtforandeVerksamhet[];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-edit-employee-form',
|
selector: 'msfa-edit-employee-form',
|
||||||
templateUrl: './edit-employee-form.component.html',
|
templateUrl: './edit-employee-form.component.html',
|
||||||
@@ -65,6 +56,7 @@ export class EditEmployeeFormComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
editEmployeeFormGroup: FormGroup | null = null;
|
editEmployeeFormGroup: FormGroup | null = null;
|
||||||
displayEditWithoutRolesDialog = false;
|
displayEditWithoutRolesDialog = false;
|
||||||
|
displayPristineWarning = false;
|
||||||
|
|
||||||
displayRolesDialog = false;
|
displayRolesDialog = false;
|
||||||
|
|
||||||
@@ -146,16 +138,22 @@ export class EditEmployeeFormComponent implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private initializeEditEmployeeFormGroup(): void {
|
private initializeEditEmployeeFormGroup(): void {
|
||||||
|
const currentTjanst = this.employee.tjanster[0];
|
||||||
|
const tjanstId = currentTjanst
|
||||||
|
? this.availableTjanster.find(tjanst => tjanst.code === currentTjanst.tjansteKod).tjanstId
|
||||||
|
: null;
|
||||||
|
|
||||||
this.editEmployeeFormGroup = new FormGroup({
|
this.editEmployeeFormGroup = new FormGroup({
|
||||||
email: new FormControl(this.employee.email, [RequiredValidator('E-postadress'), EmailValidator()]),
|
email: new FormControl(this.employee.email, [RequiredValidator('E-postadress'), EmailValidator()]),
|
||||||
tjanster: new FormControl(this.employee.tjanster[0]?.tjanstId, [RequiredValidator('Tjänst')]),
|
tjanster: new FormControl(tjanstId, [RequiredValidator('Tjänst')]),
|
||||||
roles: this.employeeFormService.getRolesFormGroup(this.availableRoles, this.employee.roles),
|
roles: this.employeeFormService.getRolesFormGroup(this.availableRoles, this.employee.roles),
|
||||||
utforandeVerksamheter: new FormControl(
|
utforandeVerksamheter: new FormControl(
|
||||||
this.utforandeVerksamheterService.getTreeNodeDataFromUtforandeVerksamheter(this.availableUtforandeVerksamheter),
|
this.utforandeVerksamheterService.getTreeNodeDataFromUtforandeVerksamheter(this.availableUtforandeVerksamheter),
|
||||||
[
|
[
|
||||||
TreeNodeValidator.IsValidTreeNode(
|
TreeNodeValidator.IsValidTreeNode(
|
||||||
this.utforandeVerksamheterService.hasSelectedUtforandeVerksamhet,
|
this.utforandeVerksamheterService.hasSelectedUtforandeVerksamhet,
|
||||||
'required'
|
'required',
|
||||||
|
this.toggleAllUtforandeVerksamhetFormControl
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@@ -180,7 +178,12 @@ export class EditEmployeeFormComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
this.editEmployeeFormGroup.markAllAsTouched();
|
this.editEmployeeFormGroup.markAllAsTouched();
|
||||||
|
|
||||||
if (this.editEmployeeFormGroup.invalid || this.editEmployeeFormGroup.pristine) {
|
if (this.editEmployeeFormGroup.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.editEmployeeFormGroup.pristine) {
|
||||||
|
this.displayPristineWarning = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +210,6 @@ export class EditEmployeeFormComponent implements OnInit, OnChanges {
|
|||||||
this.utforandeVerksamhetFormControl?.value
|
this.utforandeVerksamhetFormControl?.value
|
||||||
),
|
),
|
||||||
allaUtforandeVerksamheter: !!this.toggleAllUtforandeVerksamhetFormControl.value,
|
allaUtforandeVerksamheter: !!this.toggleAllUtforandeVerksamhetFormControl.value,
|
||||||
utforandeVerksamhetIds: [],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="employee-form__block">
|
<div class="employee-form__block">
|
||||||
<msfa-edit-employee-form
|
<msfa-edit-employee-form
|
||||||
|
*ngIf="employee && (tjanster$ | async)"
|
||||||
[employee]="employee"
|
[employee]="employee"
|
||||||
[availableRoles]="availableRoles"
|
[availableRoles]="availableRoles"
|
||||||
[availableTjanster]="tjanster$ | async"
|
[availableTjanster]="tjanster$ | async"
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
&__block {
|
&__block {
|
||||||
max-width: var(--digi--typography--text--max-width);
|
max-width: var(--digi--typography--text--max-width);
|
||||||
margin-bottom: $digi--layout--gutter--xl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__input {
|
&__input {
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ import { Employee } from '@msfa-models/employee.model';
|
|||||||
import { CustomError } from '@msfa-models/error/custom-error';
|
import { CustomError } from '@msfa-models/error/custom-error';
|
||||||
import { Role } from '@msfa-models/role.model';
|
import { Role } from '@msfa-models/role.model';
|
||||||
import { Tjanst } from '@msfa-models/tjanst.model';
|
import { Tjanst } from '@msfa-models/tjanst.model';
|
||||||
|
import { UtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model';
|
||||||
import { EmployeeService } from '@msfa-services/api/employee.service';
|
import { EmployeeService } from '@msfa-services/api/employee.service';
|
||||||
import { TjanstService } from '@msfa-services/api/tjanst.service';
|
import { TjanstService } from '@msfa-services/api/tjanst.service';
|
||||||
import {
|
import { UtforandeVerksamheterService } from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
|
||||||
UtforandeVerksamhet,
|
|
||||||
UtforandeVerksamheterService,
|
|
||||||
} from '@msfa-services/utforande-verksamheter/utforande-verksamheter.service';
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { filter, switchMap } from 'rxjs/operators';
|
import { filter, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@@ -29,7 +27,7 @@ export class EmployeeFormComponent implements OnInit {
|
|||||||
tjanster$: Observable<Tjanst[]> = this.tjanstService.tjanster$;
|
tjanster$: Observable<Tjanst[]> = this.tjanstService.tjanster$;
|
||||||
availableUtforandeVerksamheter$: Observable<UtforandeVerksamhet[]> = this._selectedTjanstIds$.pipe(
|
availableUtforandeVerksamheter$: Observable<UtforandeVerksamhet[]> = this._selectedTjanstIds$.pipe(
|
||||||
filter(selectedTjanstIds => !!selectedTjanstIds?.length),
|
filter(selectedTjanstIds => !!selectedTjanstIds?.length),
|
||||||
switchMap(selectedTjanstIds => this.utforandeVerksamheterService.getUtforandeVerksamheter(selectedTjanstIds))
|
switchMap(selectedTjanstIds => this.utforandeVerksamheterService.fetchUtforandeVerksamheter$(selectedTjanstIds))
|
||||||
);
|
);
|
||||||
|
|
||||||
availableRoles: Role[] = this.employeeService.allRoles;
|
availableRoles: Role[] = this.employeeService.allRoles;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { ReactiveFormsModule } from '@angular/forms';
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
import { HideTextModule } from '@msfa-shared/components/hide-text/hide-text.module';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
|
import { RolesDialogModule } from '@msfa-shared/components/roles-dialog/roles-dialog.module';
|
||||||
import { TreeNodesSelectorModule } from '@msfa-shared/components/tree-nodes-selector/tree-nodes-selector.module';
|
import { TreeNodesSelectorModule } from '@msfa-shared/components/tree-nodes-selector/tree-nodes-selector.module';
|
||||||
import { LocalDatePipeModule } from '@msfa-shared/pipes/local-date/local-date.module';
|
import { LocalDatePipeModule } from '@msfa-shared/pipes/local-date/local-date.module';
|
||||||
import { EmployeeDeleteModule } from '../../components/employee-delete/employee-delete.module';
|
import { EmployeeDeleteModule } from '../../components/employee-delete/employee-delete.module';
|
||||||
@@ -38,6 +39,7 @@ import { EmployeeFormComponent } from './employee-form.component';
|
|||||||
DigiNgDialogModule,
|
DigiNgDialogModule,
|
||||||
HideTextModule,
|
HideTextModule,
|
||||||
TreeNodesSelectorModule,
|
TreeNodesSelectorModule,
|
||||||
|
RolesDialogModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EmployeeFormModule {}
|
export class EmployeeFormModule {}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
|
|||||||
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 } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { By } from '@angular/platform-browser';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
import { SortOrder } from '@msfa-enums/sort-order.enum';
|
||||||
import { EmployeeCompactResponse } from '@msfa-models/api/employee.response.model';
|
import { EmployeeCompactResponse } from '@msfa-models/api/employee.response.model';
|
||||||
@@ -13,7 +12,6 @@ import { employeesMock } from './employees-list.mock';
|
|||||||
describe('EmployeesListComponent', () => {
|
describe('EmployeesListComponent', () => {
|
||||||
let component: EmployeesListComponent;
|
let component: EmployeesListComponent;
|
||||||
let fixture: ComponentFixture<EmployeesListComponent>;
|
let fixture: ComponentFixture<EmployeesListComponent>;
|
||||||
const getEmployeeRows = () => fixture.debugElement.queryAll(By.css('.employees-list__row'));
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
<h2>Personallista</h2>
|
<h2>Personallista</h2>
|
||||||
|
|
||||||
<form class="employees__search-wrapper" (ngSubmit)="setSearchFilter()">
|
<form class="employees__search-wrapper" (ngSubmit)="setSearchFilter()">
|
||||||
<!-- <digi-form-input-search
|
<digi-form-input-search
|
||||||
af-label="Sök personal"
|
af-label="Sök på personalens namn"
|
||||||
af-label-description="Sök på namn"
|
|
||||||
(afOnInput)="setSearchValue($event)"
|
(afOnInput)="setSearchValue($event)"
|
||||||
></digi-form-input-search> -->
|
></digi-form-input-search>
|
||||||
|
|
||||||
<digi-form-checkbox
|
<digi-form-checkbox
|
||||||
class="employees__only-employees-without-authorization"
|
class="employees__only-employees-without-authorization"
|
||||||
af-label="Visa endast personal utan behörigheter"
|
af-label="Visa endast personal utan behörigheter"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
&__search-wrapper {
|
&__search-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: $digi--layout--gutter--l;
|
||||||
max-width: var(--digi--typography--text--max-width);
|
max-width: var(--digi--typography--text--max-width);
|
||||||
margin-bottom: $digi--layout--gutter--xl;
|
margin-bottom: $digi--layout--gutter--xl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,15 +33,15 @@ export class AvropFiltersComponent {
|
|||||||
this.avropService.setSelectedKommuner(filterOptions);
|
this.avropService.setSelectedKommuner(filterOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeKommun(kommunToRemove: MultiselectFilterOption) {
|
removeKommun(kommunToRemove: MultiselectFilterOption): void {
|
||||||
this.avropService.removeKommun(kommunToRemove);
|
this.avropService.removeKommun(kommunToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUtforandeVerksamhet(utforandeVerksamhetToRemove: MultiselectFilterOption) {
|
removeUtforandeVerksamhet(utforandeVerksamhetToRemove: MultiselectFilterOption): void {
|
||||||
this.avropService.removeUtforandeVerksamhet(utforandeVerksamhetToRemove);
|
this.avropService.removeUtforandeVerksamhet(utforandeVerksamhetToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTjanst(tjanstToRemove: MultiselectFilterOption) {
|
removeTjanst(tjanstToRemove: MultiselectFilterOption): void {
|
||||||
this.avropService.removeTjanst(tjanstToRemove);
|
this.avropService.removeTjanst(tjanstToRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<msfa-layout>
|
|
||||||
<section class="messages">Meddelanden funkar!</section>
|
|
||||||
</msfa-layout>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
|
|
||||||
import { MessagesComponent } from './messages.component';
|
|
||||||
|
|
||||||
describe('MessagesComponent', () => {
|
|
||||||
let component: MessagesComponent;
|
|
||||||
let fixture: ComponentFixture<MessagesComponent>;
|
|
||||||
|
|
||||||
beforeEach(
|
|
||||||
waitForAsync(() => {
|
|
||||||
void TestBed.configureTestingModule({
|
|
||||||
declarations: [MessagesComponent, LayoutComponent],
|
|
||||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
|
||||||
}).compileComponents();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(MessagesComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'msfa-messages',
|
|
||||||
templateUrl: './messages.component.html',
|
|
||||||
styleUrls: ['./messages.component.scss'],
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
})
|
|
||||||
export class MessagesComponent {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
|
||||||
import { MessagesComponent } from './messages.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [MessagesComponent],
|
|
||||||
imports: [CommonModule, RouterModule.forChild([{ path: '', component: MessagesComponent }]), LayoutModule],
|
|
||||||
})
|
|
||||||
export class MessagesModule {}
|
|
||||||
@@ -1,9 +1,29 @@
|
|||||||
<msfa-layout>
|
<msfa-layout>
|
||||||
<digi-typography>
|
<digi-typography>
|
||||||
<section class="my-account">
|
<section class="my-account">
|
||||||
<h1>Mitt konto</h1>
|
<header class="my-account__header">
|
||||||
|
<h1>Mitt konto</h1>
|
||||||
|
<a class="my-account__logout" routerLink="/logga-ut">
|
||||||
|
<msfa-icon [icon]="IconType.LOGOUT"></msfa-icon>
|
||||||
|
Logga ut
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
|
||||||
<digi-ng-link-internal afText="Logga ut" afRoute="/logga-ut"></digi-ng-link-internal>
|
<main *ngIf="user$ | async as user; else loadingRef">
|
||||||
|
<p>Här kan du se dina uppgifter.</p>
|
||||||
|
|
||||||
|
<h2>Mina roller</h2>
|
||||||
|
<ul class="my-account__roles">
|
||||||
|
<li class="my-account__role" *ngFor="let role of user.roles">
|
||||||
|
<digi-icon-check-circle class="msfa__digi-icon my-account__authorization-icon"></digi-icon-check-circle>
|
||||||
|
{{role.name}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
</section>
|
</section>
|
||||||
</digi-typography>
|
</digi-typography>
|
||||||
</msfa-layout>
|
</msfa-layout>
|
||||||
|
|
||||||
|
<ng-template #loadingRef>
|
||||||
|
<digi-ng-skeleton-base [afCount]="3" afText="Laddar kontoinformation"></digi-ng-skeleton-base>
|
||||||
|
</ng-template>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
@import 'mixins/buttons';
|
||||||
|
@import 'mixins/list';
|
||||||
|
@import 'variables/gutters';
|
||||||
|
|
||||||
|
.my-account {
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__logout {
|
||||||
|
@include msfa__button('secondary');
|
||||||
|
}
|
||||||
|
|
||||||
|
&__roles {
|
||||||
|
@include msfa__reset-list;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $digi--layout--gutter--s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__role {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $digi--layout--gutter--s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__authorization-icon {
|
||||||
|
color: var(--digi--ui--color--border--success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
|
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { MyAccountComponent } from './my-account.component';
|
import { MyAccountComponent } from './my-account.component';
|
||||||
|
|
||||||
describe('MyAccountComponent', () => {
|
describe('MyAccountComponent', () => {
|
||||||
@@ -9,8 +11,9 @@ describe('MyAccountComponent', () => {
|
|||||||
beforeEach(
|
beforeEach(
|
||||||
waitForAsync(() => {
|
waitForAsync(() => {
|
||||||
void TestBed.configureTestingModule({
|
void TestBed.configureTestingModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [MyAccountComponent],
|
declarations: [MyAccountComponent],
|
||||||
|
imports: [HttpClientTestingModule, RouterTestingModule],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||||
|
import { User } from '@msfa-models/user.model';
|
||||||
|
import { UserService } from '@msfa-services/api/user.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-my-account',
|
selector: 'msfa-my-account',
|
||||||
@@ -6,4 +10,9 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
styleUrls: ['./my-account.component.scss'],
|
styleUrls: ['./my-account.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class MyAccountComponent {}
|
export class MyAccountComponent {
|
||||||
|
user$: Observable<User> = this.userService.user$;
|
||||||
|
readonly IconType = IconType;
|
||||||
|
|
||||||
|
constructor(private userService: UserService) {}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { DigiNgLinkInternalModule } from '@af/digi-ng/_link/link-internal';
|
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
|
||||||
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 { RouterModule } from '@angular/router';
|
||||||
|
import { IconModule } from '@msfa-shared/components/icon/icon.module';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
import { MyAccountComponent } from './my-account.component';
|
import { MyAccountComponent } from './my-account.component';
|
||||||
|
|
||||||
@@ -12,7 +13,8 @@ import { MyAccountComponent } from './my-account.component';
|
|||||||
CommonModule,
|
CommonModule,
|
||||||
RouterModule.forChild([{ path: '', component: MyAccountComponent }]),
|
RouterModule.forChild([{ path: '', component: MyAccountComponent }]),
|
||||||
LayoutModule,
|
LayoutModule,
|
||||||
DigiNgLinkInternalModule,
|
IconModule,
|
||||||
|
DigiNgSkeletonBaseModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class MyAccountModule {}
|
export class MyAccountModule {}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
|||||||
import { Organization } from '@msfa-models/organization.model';
|
import { Organization } from '@msfa-models/organization.model';
|
||||||
import { UserService } from '@msfa-services/api/user.service';
|
import { UserService } from '@msfa-services/api/user.service';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { filter, map } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-organization-picker',
|
selector: 'msfa-organization-picker',
|
||||||
@@ -14,9 +14,8 @@ import { filter, map } from 'rxjs/operators';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class OrganizationPickerComponent extends UnsubscribeDirective {
|
export class OrganizationPickerComponent extends UnsubscribeDirective {
|
||||||
organizations$: Observable<Organization[]> = this.userService.user$.pipe(
|
organizations$: Observable<Organization[]> = this.userService.organizations$.pipe(
|
||||||
filter(user => !!(user && user.organizations?.length)),
|
filter(organizations => !!organizations?.length)
|
||||||
map(({ organizations }) => organizations)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
<msfa-layout>
|
<msfa-layout [showBreadCrumbs]="false">
|
||||||
<digi-typography>
|
<digi-typography>
|
||||||
<section class="page-not-found">
|
<section class="page-not-found">
|
||||||
<h1>Oj då! Vi kan inte hitta sidan.</h1>
|
<h1>Oj då! Vi kan inte hitta sidan.</h1>
|
||||||
<p>Det kan bero på att länken du använder är felaktig eller att sidan inte längre finns.</p>
|
<p>Det kan bero på att länken du använder är felaktig eller att sidan inte längre finns.</p>
|
||||||
<a class="msfa__link msfa__link--with-icon msfa__link--ignore-visited" routerLink="/">
|
<msfa-back-link route="/">Tillbaka till startsidan</msfa-back-link>
|
||||||
<digi-icon-arrow-left class="msfa__digi-icon"></digi-icon-arrow-left>
|
|
||||||
Gå tillbaka till startsidan
|
|
||||||
</a>
|
|
||||||
</section>
|
</section>
|
||||||
</digi-typography>
|
</digi-typography>
|
||||||
</msfa-layout>
|
</msfa-layout>
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
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 { RouterModule } from '@angular/router';
|
||||||
|
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
import { PageNotFoundComponent } from './page-not-found.component';
|
import { PageNotFoundComponent } from './page-not-found.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [PageNotFoundComponent],
|
declarations: [PageNotFoundComponent],
|
||||||
imports: [CommonModule, RouterModule.forChild([{ path: '', component: PageNotFoundComponent }]), LayoutModule],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule.forChild([{ path: '', component: PageNotFoundComponent }]),
|
||||||
|
LayoutModule,
|
||||||
|
BackLinkModule,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export class PageNotFoundModule {}
|
export class PageNotFoundModule {}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<msfa-layout>
|
|
||||||
<section class="settings">Inställningar funkar!</section>
|
|
||||||
</msfa-layout>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
|
|
||||||
import { SettingsComponent } from './settings.component';
|
|
||||||
|
|
||||||
describe('SettingsComponent', () => {
|
|
||||||
let component: SettingsComponent;
|
|
||||||
let fixture: ComponentFixture<SettingsComponent>;
|
|
||||||
|
|
||||||
beforeEach(
|
|
||||||
waitForAsync(() => {
|
|
||||||
void TestBed.configureTestingModule({
|
|
||||||
declarations: [SettingsComponent, LayoutComponent],
|
|
||||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
|
||||||
}).compileComponents();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(SettingsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'msfa-settings',
|
|
||||||
templateUrl: './settings.component.html',
|
|
||||||
styleUrls: ['./settings.component.scss'],
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
})
|
|
||||||
export class SettingsComponent {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
|
||||||
import { SettingsComponent } from './settings.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [SettingsComponent],
|
|
||||||
imports: [CommonModule, RouterModule.forChild([{ path: '', component: SettingsComponent }]), LayoutModule],
|
|
||||||
})
|
|
||||||
export class SettingsModule {}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<msfa-layout>
|
|
||||||
<section class="statistics">Statistik funkar!</section>
|
|
||||||
</msfa-layout>
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
|
||||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
|
||||||
import { LayoutComponent } from '@msfa-shared/components/layout/layout.component';
|
|
||||||
import { StatisticsComponent } from './statistics.component';
|
|
||||||
|
|
||||||
describe('StatisticsComponent', () => {
|
|
||||||
let component: StatisticsComponent;
|
|
||||||
let fixture: ComponentFixture<StatisticsComponent>;
|
|
||||||
|
|
||||||
beforeEach(
|
|
||||||
waitForAsync(() => {
|
|
||||||
void TestBed.configureTestingModule({
|
|
||||||
declarations: [StatisticsComponent, LayoutComponent],
|
|
||||||
imports: [RouterTestingModule, HttpClientTestingModule],
|
|
||||||
}).compileComponents();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(StatisticsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'msfa-statistics',
|
|
||||||
templateUrl: './statistics.component.html',
|
|
||||||
styleUrls: ['./statistics.component.scss'],
|
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
})
|
|
||||||
export class StatisticsComponent {}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { RouterModule } from '@angular/router';
|
|
||||||
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
|
||||||
import { StatisticsComponent } from './statistics.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [StatisticsComponent],
|
|
||||||
imports: [CommonModule, RouterModule.forChild([{ path: '', component: StatisticsComponent }]), LayoutModule],
|
|
||||||
})
|
|
||||||
export class StatisticsModule {}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<msfa-layout>
|
||||||
|
<digi-typography>
|
||||||
|
<section class="unauthorized">
|
||||||
|
<h1>Du saknar behörigheter!</h1>
|
||||||
|
<p>
|
||||||
|
Det verkar som att du saknar behörigheter att komma in på sidan. Kontakta verksamhetens
|
||||||
|
behörighetsadministratör.
|
||||||
|
</p>
|
||||||
|
<msfa-back-link route="/">Tillbaka till startsidan</msfa-back-link>
|
||||||
|
</section>
|
||||||
|
</digi-typography>
|
||||||
|
</msfa-layout>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
|
import { UnauthorizedComponent } from './unauthorized.component';
|
||||||
|
|
||||||
|
describe('UnauthorizedComponent', () => {
|
||||||
|
let component: UnauthorizedComponent;
|
||||||
|
let fixture: ComponentFixture<UnauthorizedComponent>;
|
||||||
|
|
||||||
|
beforeEach(
|
||||||
|
waitForAsync(() => {
|
||||||
|
void TestBed.configureTestingModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [UnauthorizedComponent],
|
||||||
|
imports: [RouterTestingModule],
|
||||||
|
}).compileComponents();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(UnauthorizedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'msfa-unauthorized',
|
||||||
|
templateUrl: './unauthorized.component.html',
|
||||||
|
styleUrls: ['./unauthorized.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class UnauthorizedComponent {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { BackLinkModule } from '@msfa-shared/components/back-link/back-link.module';
|
||||||
|
import { LayoutModule } from '@msfa-shared/components/layout/layout.module';
|
||||||
|
import { UnauthorizedComponent } from './unauthorized.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [UnauthorizedComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
RouterModule.forChild([{ path: '', component: UnauthorizedComponent }]),
|
||||||
|
LayoutModule,
|
||||||
|
BackLinkModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class UnauthorizedModule {}
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
.back-link {
|
@import 'mixins/link';
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: var(--digi--typography--font-weight--semibold);
|
|
||||||
gap: var(--digi--layout--gutter--xs);
|
|
||||||
|
|
||||||
&:hover {
|
.back-link {
|
||||||
text-decoration: underline;
|
@include msfa__link(true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,26 @@
|
|||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
*ngIf="icon === iconType.BUILDING"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 448 512"
|
||||||
|
width="25"
|
||||||
|
height="25"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M436 480h-20V24c0-13.255-10.745-24-24-24H56C42.745 0 32 10.745 32 24v456H12c-6.627 0-12 5.373-12 12v20h448v-20c0-6.627-5.373-12-12-12zM128 76c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12V76zm0 96c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12v-40zm52 148h-40c-6.627 0-12-5.373-12-12v-40c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40c0 6.627-5.373 12-12 12zm76 160h-64v-84c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v84zm64-172c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12v-40c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40zm0-96c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12v-40c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40zm0-96c0 6.627-5.373 12-12 12h-40c-6.627 0-12-5.373-12-12V76c0-6.627 5.373-12 12-12h40c6.627 0 12 5.373 12 12v40z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg *ngIf="icon === iconType.LOGOUT" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="25" height="25">
|
||||||
|
<path
|
||||||
|
d="M497 273L329 441c-15 15-41 4.5-41-17v-96H152c-13.3 0-24-10.7-24-24v-96c0-13.3 10.7-24 24-24h136V88c0-21.4 25.9-32 41-17l168 168c9.3 9.4 9.3 24.6 0 34zM192 436v-40c0-6.6-5.4-12-12-12H96c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h84c6.6 0 12-5.4 12-12V76c0-6.6-5.4-12-12-12H96c-53 0-96 43-96 96v192c0 53 43 96 96 96h84c6.6 0 12-5.4 12-12z"
|
||||||
|
fill="currentColor"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<svg
|
<svg
|
||||||
*ngIf="icon === iconType.CLIPBOARD"
|
*ngIf="icon === iconType.CLIPBOARD"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -2,7 +2,14 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c
|
|||||||
import { IconSize } from '@msfa-enums/icon-size.enum';
|
import { IconSize } from '@msfa-enums/icon-size.enum';
|
||||||
import { IconType } from '@msfa-enums/icon-type.enum';
|
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||||
|
|
||||||
const CUSTOM_ICONS: IconType[] = [IconType.HOME, IconType.SETTINGS, IconType.PLUS, IconType.CLIPBOARD];
|
const CUSTOM_ICONS: IconType[] = [
|
||||||
|
IconType.HOME,
|
||||||
|
IconType.SETTINGS,
|
||||||
|
IconType.PLUS,
|
||||||
|
IconType.CLIPBOARD,
|
||||||
|
IconType.BUILDING,
|
||||||
|
IconType.LOGOUT,
|
||||||
|
];
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-icon',
|
selector: 'msfa-icon',
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="footer__logo-wrapper">
|
<div class="footer__logo-wrapper">
|
||||||
<a class="footer__logo-link" href="/">
|
<digi-logo af-color="secondary"></digi-logo>
|
||||||
<img class="footer__logo" src="/assets/logo/arbetsformedlingen-light.svg" alt="Arbetsförmedlingen" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
|
@import 'variables/gutters';
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
background-color: var(--digi--ui--color--background--profile);
|
background-color: var(--digi--ui--color--background--profile);
|
||||||
padding: var(--digi--layout--gutter);
|
padding: $digi--layout--gutter--l $digi--layout--gutter;
|
||||||
|
|
||||||
&__logo-wrapper {
|
&__logo-wrapper {
|
||||||
height: 100%;
|
::ng-deep .digi-logo {
|
||||||
display: flex;
|
--digi-logo--padding: 0;
|
||||||
align-items: center;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&__logo {
|
|
||||||
height: 2rem;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { FooterComponent } from './footer.component';
|
import { FooterComponent } from './footer.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
declarations: [FooterComponent],
|
declarations: [FooterComponent],
|
||||||
imports: [CommonModule, RouterModule],
|
imports: [CommonModule, RouterModule],
|
||||||
exports: [FooterComponent],
|
exports: [FooterComponent],
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
<div class="navigation">
|
<div class="navigation">
|
||||||
<div class="navigation__logo-wrapper">
|
<div class="navigation__logo-wrapper">
|
||||||
<a [routerLink]="['/']" aria-label="Till startsidan för FA Mina sidor">
|
<a class="navigation__logo-link" [routerLink]="['/']" aria-label="Till startsidan för FA Mina sidor">
|
||||||
<digi-logo af-system-name="Mina sidor för fristående aktörer" af-color="secondary"></digi-logo>
|
<digi-logo af-system-name="Mina sidor för fristående aktörer" af-color="secondary"></digi-logo>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<ul class="navigation__list msfa__hide-on-print">
|
<ul class="navigation__list msfa__hide-on-print" *ngIf="user">
|
||||||
<li *ngIf="user" class="navigation__item">
|
<li class="navigation__item">
|
||||||
<a routerLink="/mitt-konto" class="navigation__link">
|
<a routerLink="/mitt-konto" class="navigation__link">
|
||||||
<msfa-icon [icon]="iconType.USER" size="l"></msfa-icon>
|
<msfa-icon [icon]="iconType.USER" size="l"></msfa-icon>
|
||||||
<span class="navigation__text">{{ user.fullName }}</span>
|
<span class="navigation__text">{{ user.fullName }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li *ngIf="selectedOrganization" class="navigation__item navigation__item--without-link">
|
||||||
|
<msfa-icon [icon]="iconType.BUILDING" size="l"></msfa-icon>
|
||||||
|
<span class="navigation__text">{{ selectedOrganization.name }}</span>
|
||||||
|
</li>
|
||||||
<!-- <li class="navigation__item">
|
<!-- <li class="navigation__item">
|
||||||
<a routerLink="/" class="navigation__link">
|
<a routerLink="/" class="navigation__link">
|
||||||
<msfa-icon [icon]="iconType.BELL" size="l"></msfa-icon>
|
<msfa-icon [icon]="iconType.BELL" size="l"></msfa-icon>
|
||||||
|
|||||||
@@ -24,6 +24,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__logo-link {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
::ng-deep .digi-logo {
|
||||||
|
--digi-logo--padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__logo {
|
&__logo {
|
||||||
height: $msfa__navigation-height / 2.5;
|
height: $msfa__navigation-height / 2.5;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@@ -37,16 +45,15 @@
|
|||||||
@include msfa__reset-list;
|
@include msfa__reset-list;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
gap: $digi--layout--gutter--l;
|
gap: $digi--layout--gutter;
|
||||||
color: var(--digi--typography--color--text--light);
|
color: var(--digi--typography--color--text--light);
|
||||||
margin-right: var(--digi--layout--gutter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__user,
|
&__item--without-link,
|
||||||
&__link {
|
&__link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||||
import { IconType } from '@msfa-enums/icon-type.enum';
|
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||||
|
import { Organization } from '@msfa-models/organization.model';
|
||||||
import { User } from '@msfa-models/user.model';
|
import { User } from '@msfa-models/user.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -10,5 +11,6 @@ import { User } from '@msfa-models/user.model';
|
|||||||
})
|
})
|
||||||
export class NavigationComponent {
|
export class NavigationComponent {
|
||||||
@Input() user: User;
|
@Input() user: User;
|
||||||
|
@Input() selectedOrganization: Organization;
|
||||||
iconType = IconType;
|
iconType = IconType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,20 +11,20 @@
|
|||||||
Hem
|
Hem
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar__item">
|
<li class="sidebar__item" *ngIf="isReceiveDeltagare">
|
||||||
<a [routerLink]="['/nya-deltagare']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
<a [routerLink]="['/nya-deltagare']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
||||||
<msfa-icon class="sidebar__icon" [icon]="iconType.CLIPBOARD" size="xl"></msfa-icon>
|
<msfa-icon class="sidebar__icon" [icon]="iconType.CLIPBOARD" size="xl"></msfa-icon>
|
||||||
Nya deltagare
|
Nya deltagare
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="sidebar__item">
|
<li class="sidebar__item" *ngIf="isReportAndPlanning">
|
||||||
<a [routerLink]="['/deltagare']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
<a [routerLink]="['/deltagare']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
||||||
<msfa-icon class="sidebar__icon" [icon]="iconType.SOK_KANDIDAT" size="xl"></msfa-icon>
|
<msfa-icon class="sidebar__icon" [icon]="iconType.SOK_KANDIDAT" size="xl"></msfa-icon>
|
||||||
Deltagarlista
|
Deltagarlista
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="sidebar__item">
|
<li class="sidebar__item" *ngIf="isAuthAdmin">
|
||||||
<a [routerLink]="['/administration']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
<a [routerLink]="['/administration']" [routerLinkActive]="['sidebar__link--active']" class="sidebar__link">
|
||||||
<msfa-icon class="sidebar__icon" [icon]="iconType.SETTINGS" size="xl"></msfa-icon>
|
<msfa-icon class="sidebar__icon" [icon]="iconType.SETTINGS" size="xl"></msfa-icon>
|
||||||
Administration
|
Administration
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||||
import { IconType } from '@msfa-enums/icon-type.enum';
|
import { IconType } from '@msfa-enums/icon-type.enum';
|
||||||
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
|
import { Role } from '@msfa-models/role.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-sidebar',
|
selector: 'msfa-sidebar',
|
||||||
@@ -8,5 +10,16 @@ import { IconType } from '@msfa-enums/icon-type.enum';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class SidebarComponent {
|
export class SidebarComponent {
|
||||||
|
@Input() userRoles: Role[];
|
||||||
iconType = IconType;
|
iconType = IconType;
|
||||||
|
|
||||||
|
get isAuthAdmin(): boolean {
|
||||||
|
return this.userRoles?.some(role => role.type === RoleEnum.MSFA_AuthAdmin);
|
||||||
|
}
|
||||||
|
get isReceiveDeltagare(): boolean {
|
||||||
|
return this.userRoles?.some(role => role.type === RoleEnum.MSFA_ReceiveDeltagare);
|
||||||
|
}
|
||||||
|
get isReportAndPlanning(): boolean {
|
||||||
|
return this.userRoles?.some(role => role.type === RoleEnum.MSFA_ReportAndPlanning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
<msfa-skip-to-content mainContentId="msfa-main-content"></msfa-skip-to-content>
|
<msfa-skip-to-content mainContentId="msfa-main-content"></msfa-skip-to-content>
|
||||||
|
|
||||||
<header class="msfa__header">
|
<header class="msfa__header">
|
||||||
<msfa-navigation [user]="user$ | async"></msfa-navigation>
|
<msfa-navigation [user]="user$ | async" [selectedOrganization]="selectedOrganization$ | async"></msfa-navigation>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<msfa-sidebar class="msfa__sidebar"></msfa-sidebar>
|
<msfa-sidebar class="msfa__sidebar" [userRoles]="roles$ | async"></msfa-sidebar>
|
||||||
<main id="msfa-main-content" class="msfa__content">
|
<main id="msfa-main-content" class="msfa__content">
|
||||||
<digi-ng-navigation-breadcrumbs
|
<digi-ng-navigation-breadcrumbs
|
||||||
|
*ngIf="showBreadCrumbs"
|
||||||
class="msfa__breadcrumbs"
|
class="msfa__breadcrumbs"
|
||||||
[afItems]="breadcrumbsItems"
|
[afItems]="breadcrumbsItems"
|
||||||
></digi-ng-navigation-breadcrumbs>
|
></digi-ng-navigation-breadcrumbs>
|
||||||
|
|||||||
@@ -43,6 +43,5 @@
|
|||||||
&__footer {
|
&__footer {
|
||||||
grid-area: footer;
|
grid-area: footer;
|
||||||
background-color: var(--digi--ui--color--primary);
|
background-color: var(--digi--ui--color--primary);
|
||||||
min-height: 10rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { NavigationBreadcrumbsItem } from '@af/digi-ng/_navigation/navigation-breadcrumbs';
|
import { NavigationBreadcrumbsItem } from '@af/digi-ng/_navigation/navigation-breadcrumbs';
|
||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
import { ActivatedRoute, NavigationEnd, Router } from '@angular/router';
|
||||||
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
|
||||||
|
import { Organization } from '@msfa-models/organization.model';
|
||||||
|
import { Role } from '@msfa-models/role.model';
|
||||||
import { User } from '@msfa-models/user.model';
|
import { User } from '@msfa-models/user.model';
|
||||||
import { AuthenticationService } from '@msfa-services/api/authentication.service';
|
import { AuthenticationService } from '@msfa-services/api/authentication.service';
|
||||||
import { UserService } from '@msfa-services/api/user.service';
|
import { UserService } from '@msfa-services/api/user.service';
|
||||||
import { mapPathsToBreadcrumbs } from '@msfa-utils/map-paths-to-breadcrumbs.util';
|
import { mapPathsToBreadcrumbs } from '@msfa-utils/map-paths-to-breadcrumbs.util';
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
import { filter, switchMap } from 'rxjs/operators';
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'msfa-layout',
|
selector: 'msfa-layout',
|
||||||
@@ -17,15 +19,18 @@ import { filter, switchMap } from 'rxjs/operators';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class LayoutComponent extends UnsubscribeDirective {
|
export class LayoutComponent extends UnsubscribeDirective {
|
||||||
private startBreadcrumb: NavigationBreadcrumbsItem = {
|
@Input() showBreadCrumbs = true;
|
||||||
|
private readonly _startBreadcrumb: NavigationBreadcrumbsItem = {
|
||||||
text: 'Start',
|
text: 'Start',
|
||||||
routerLink: '/',
|
routerLink: '/',
|
||||||
};
|
};
|
||||||
private _breadcrumbsItems$ = new BehaviorSubject<NavigationBreadcrumbsItem[]>([this.startBreadcrumb]);
|
private _breadcrumbsItems$ = new BehaviorSubject<NavigationBreadcrumbsItem[]>([this._startBreadcrumb]);
|
||||||
isLoggedIn$: Observable<boolean> = this.authenticationService.isLoggedIn$;
|
isLoggedIn$: Observable<boolean> = this.authenticationService.isLoggedIn$;
|
||||||
user$: Observable<User> = this.isLoggedIn$.pipe(
|
selectedOrganization$: Observable<Organization> = this.userService.selectedOrganization$;
|
||||||
filter(loggedIn => !!loggedIn),
|
user$: Observable<User> = this.userService.user$;
|
||||||
switchMap(() => this.userService.user$)
|
roles$: Observable<Role[]> = this.user$.pipe(
|
||||||
|
filter(user => !!user),
|
||||||
|
map(user => user.roles)
|
||||||
);
|
);
|
||||||
|
|
||||||
get breadcrumbsItems(): NavigationBreadcrumbsItem[] {
|
get breadcrumbsItems(): NavigationBreadcrumbsItem[] {
|
||||||
@@ -62,7 +67,7 @@ export class LayoutComponent extends UnsubscribeDirective {
|
|||||||
.toString()
|
.toString()
|
||||||
.split('/')
|
.split('/')
|
||||||
.filter(path => !!path);
|
.filter(path => !!path);
|
||||||
this._breadcrumbsItems$.next(mapPathsToBreadcrumbs(paths, this.startBreadcrumb));
|
this._breadcrumbsItems$.next(mapPathsToBreadcrumbs(paths, this._startBreadcrumb));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<button
|
||||||
|
class="roles-dialog-button"
|
||||||
|
type="button"
|
||||||
|
(click)="openRolesDialog()"
|
||||||
|
aria-controls="roles-dialog"
|
||||||
|
aria-label="Öppnar en dialog med information om behörigheter"
|
||||||
|
>
|
||||||
|
{{buttonText}}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<digi-ng-dialog
|
||||||
|
id="roles-dialog"
|
||||||
|
class="roles-dialog"
|
||||||
|
[afActive]="displayRolesDialog"
|
||||||
|
(afOnInactive)="closeRolesDialog()"
|
||||||
|
(afOnPrimaryClick)="closeRolesDialog()"
|
||||||
|
afHeading="Om rollerna i systemet"
|
||||||
|
afHeadingLevel="h2"
|
||||||
|
afPrimaryButtonText="Stäng"
|
||||||
|
afSecondaryButtonText=""
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
Läs beskrivningarna nedan för att lära dig mer om de olika rollerna. Personalen kan tilldelas en roll, eller flera
|
||||||
|
roller, beroende på vad de arbetar med. Tänk på att rollen endast gäller inom de utförande verksamheter och adresser
|
||||||
|
som personalen fått behörighet till.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
All personal kommer att kunna se sitt eget personalkonto, där de kan se vilka roller och utförande verksamheter och
|
||||||
|
adresser som tilldelats dem i systemet. De kommer även att se startsidan.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Administrera behörigheter</h3>
|
||||||
|
<p>
|
||||||
|
Rollen passar personal som ska administrera behörigheter i systemet. Rollen bör begränsas till ett fåtal personer
|
||||||
|
och kan användas av exempelvis firmatecknare, behörighetsadministratör, eller annan person som ska kunna
|
||||||
|
administrera personalens behörigheter. Rollen gäller endast inom de utförande verksamheter och adresser som getts
|
||||||
|
behörighet till.
|
||||||
|
</p>
|
||||||
|
<p>Rollen ger tillgång till följande funktioner:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Skapa nya personalkonton</li>
|
||||||
|
<li>Se personallista</li>
|
||||||
|
<li>Se och ändra personalkonto och dess behörigheter</li>
|
||||||
|
<li>Ta bort personalkonton</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Ta emot nya deltagare</h3>
|
||||||
|
<p>
|
||||||
|
Rollen passar personal som ska se nya deltagare som inkommit i systemet och som ska tilldela handledare till nya
|
||||||
|
deltagare. Rollen kan exempelvis användas av samordnande roller, handledare, administratörer, eller annan personal
|
||||||
|
som ska kunna utföra dessa arbetsuppgifter. Rollen gäller endast inom de utförande verksamheter och adresser som
|
||||||
|
getts behörighet till.
|
||||||
|
</p>
|
||||||
|
<p>Rollen ger tillgång till följande funktioner:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Se lista över nya deltagare som inkommit</li>
|
||||||
|
<li>Tilldela handledare till nya deltagare</li>
|
||||||
|
<li>Ta bort nya deltagare där beslut om avbrott skett innan tjänsten startat</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Rapportering, planering och information om deltagare</h3>
|
||||||
|
<p>
|
||||||
|
Rollen passar personal som arbetar nära deltagare. Rollen kan användas av exempelvis handledare, coacher, studie-
|
||||||
|
och yrkesvägledare, lärare eller andra roller som behöver se information om deltagare, planera aktiviteter med
|
||||||
|
deltagare eller hantera deltagares rapporter. Rollen gäller endast inom de utförande verksamheter och adresser som
|
||||||
|
getts behörighet till.
|
||||||
|
</p>
|
||||||
|
<p>Rollen ger tillgång till följande funktioner:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Se lista över deltagare</li>
|
||||||
|
<li>Se information om deltagare</li>
|
||||||
|
<li>Planera aktiviteter i en gemensam planering</li>
|
||||||
|
<li>Skicka och se avvikelserapporter</li>
|
||||||
|
<li>Skicka och se resultatrapporter</li>
|
||||||
|
<li>Skicka och se slutredovisningar</li>
|
||||||
|
<li>Skicka och se informativa rapporter</li>
|
||||||
|
</ul>
|
||||||
|
</digi-ng-dialog>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
@import 'mixins/link';
|
||||||
|
|
||||||
|
.roles-dialog {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roles-dialog-button {
|
||||||
|
@include msfa__button-as-link(true);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/* tslint:disable:no-unused-variable */
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { RolesDialogComponent } from './roles-dialog.component';
|
||||||
|
|
||||||
|
describe('RolesDialogComponent', () => {
|
||||||
|
let component: RolesDialogComponent;
|
||||||
|
let fixture: ComponentFixture<RolesDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
void TestBed.configureTestingModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [RolesDialogComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RolesDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'msfa-roles-dialog',
|
||||||
|
templateUrl: './roles-dialog.component.html',
|
||||||
|
styleUrls: ['./roles-dialog.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class RolesDialogComponent {
|
||||||
|
@Input() buttonText = 'Läs mer om roller här';
|
||||||
|
|
||||||
|
displayRolesDialog = false;
|
||||||
|
|
||||||
|
openRolesDialog(): void {
|
||||||
|
this.displayRolesDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeRolesDialog(): void {
|
||||||
|
this.displayRolesDialog = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { DigiNgDialogModule } from '@af/digi-ng/_dialog/dialog';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||||
|
import { RolesDialogComponent } from './roles-dialog.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||||
|
declarations: [RolesDialogComponent],
|
||||||
|
imports: [CommonModule, DigiNgDialogModule],
|
||||||
|
exports: [RolesDialogComponent],
|
||||||
|
})
|
||||||
|
export class RolesDialogModule {}
|
||||||
@@ -7,4 +7,5 @@ export const NAVIGATION = {
|
|||||||
'planera-aktiviteter': 'Planera aktiviteter',
|
'planera-aktiviteter': 'Planera aktiviteter',
|
||||||
'mitt-konto': 'Mitt konto',
|
'mitt-konto': 'Mitt konto',
|
||||||
'skapa-personalkonto': 'Skapa personalkonto',
|
'skapa-personalkonto': 'Skapa personalkonto',
|
||||||
|
obehorig: 'Saknar behörigheter',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ export enum IconType {
|
|||||||
SETTINGS = 'settings', // Custom
|
SETTINGS = 'settings', // Custom
|
||||||
PLUS = 'plus', // Custom
|
PLUS = 'plus', // Custom
|
||||||
CLIPBOARD = 'clipboard', // Custom
|
CLIPBOARD = 'clipboard', // Custom
|
||||||
|
BUILDING = 'building', // Custom
|
||||||
|
LOGOUT = 'logout', // Custom
|
||||||
USER = 'user',
|
USER = 'user',
|
||||||
USERS = 'users',
|
USERS = 'users',
|
||||||
BELL = 'bell',
|
BELL = 'bell',
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Injectable } from '@angular/core';
|
|||||||
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { AuthenticationService } from '@msfa-services/api/authentication.service';
|
import { AuthenticationService } from '@msfa-services/api/authentication.service';
|
||||||
import { UserService } from '@msfa-services/api/user.service';
|
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { map, switchMap } from 'rxjs/operators';
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
@@ -10,24 +9,13 @@ import { map, switchMap } from 'rxjs/operators';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AuthGuard implements CanActivate {
|
export class AuthGuard implements CanActivate {
|
||||||
constructor(
|
constructor(private authenticationService: AuthenticationService, private router: Router) {}
|
||||||
private authenticationService: AuthenticationService,
|
|
||||||
private router: Router,
|
|
||||||
private userService: UserService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
|
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||||
return this.authenticationService.isLoggedIn$.pipe(
|
return this.authenticationService.isLoggedIn$.pipe(
|
||||||
switchMap(loggedIn => {
|
switchMap(loggedIn => {
|
||||||
if (loggedIn) {
|
if (loggedIn) {
|
||||||
return this.userService.selectedOrganization$.pipe(
|
return of(true);
|
||||||
map(organization => {
|
|
||||||
if (!organization) {
|
|
||||||
void this.router.navigateByUrl(`/organization-picker`);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else if (route.queryParams.code) {
|
} else if (route.queryParams.code) {
|
||||||
return this.authenticationService.login$(route.queryParams.code).pipe(map(result => !!result));
|
return this.authenticationService.login$(route.queryParams.code).pipe(map(result => !!result));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CanActivate, Router } from '@angular/router';
|
||||||
|
import { UserService } from '@msfa-services/api/user.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class OrganizationGuard implements CanActivate {
|
||||||
|
constructor(private router: Router, private userService: UserService) {}
|
||||||
|
|
||||||
|
canActivate(): Observable<boolean> {
|
||||||
|
return this.userService.selectedOrganization$.pipe(
|
||||||
|
map(organization => {
|
||||||
|
if (!organization) {
|
||||||
|
void this.router.navigateByUrl(`/organization-picker`);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
Normal file
30
apps/mina-sidor-fa/src/app/shared/guards/role.guard.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
|
||||||
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
|
import { UserService } from '@msfa-services/api/user.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { filter, map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class RoleGuard implements CanActivate {
|
||||||
|
constructor(private router: Router, private userService: UserService) {}
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
|
||||||
|
const expectedRole: RoleEnum = route.data.expectedRole as RoleEnum;
|
||||||
|
|
||||||
|
return this.userService.user$.pipe(
|
||||||
|
filter(user => !!user),
|
||||||
|
map(({ roles }) => {
|
||||||
|
const userHasRole = roles.some(role => role.type === expectedRole);
|
||||||
|
|
||||||
|
if (userHasRole) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void this.router.navigateByUrl('/obehorig');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface EmployeeAdressResponse {
|
||||||
|
id: number;
|
||||||
|
namn: string;
|
||||||
|
}
|
||||||
@@ -5,6 +5,5 @@ export interface EmployeeEditRequest {
|
|||||||
roles: RoleEnum[];
|
roles: RoleEnum[];
|
||||||
tjanstIds: number[];
|
tjanstIds: number[];
|
||||||
allaUtforandeVerksamheter: boolean;
|
allaUtforandeVerksamheter: boolean;
|
||||||
utforandeVerksamhetIds?: number[];
|
|
||||||
adressIds: number[];
|
adressIds: number[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface EmployeeTjanstResponse {
|
||||||
|
namn: string;
|
||||||
|
tjansteKod: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { EmployeeAdressResponse } from './employee-adress.response.model';
|
||||||
|
|
||||||
|
export interface EmployeeUtforandeVerksamhetResponse {
|
||||||
|
id: number;
|
||||||
|
namn: string;
|
||||||
|
allaAdresser: boolean;
|
||||||
|
adresser: EmployeeAdressResponse[];
|
||||||
|
}
|
||||||
@@ -1,18 +1,7 @@
|
|||||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
|
import { PaginationMeta } from '@msfa-models/pagination-meta.model';
|
||||||
import { TjanstResponse } from './tjanst.response.model';
|
import { EmployeeTjanstResponse } from './employee-tjanst.response.model';
|
||||||
|
import { EmployeeUtforandeVerksamhetResponse } from './employee-utforande-verksamhet.response.model';
|
||||||
interface UtforandeVerksamhetResponse {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
allaAdresser: boolean;
|
|
||||||
adresser?: AdressResponse[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AdressResponse {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EmployeeCompactResponse {
|
export interface EmployeeCompactResponse {
|
||||||
ciamUserId: string;
|
ciamUserId: string;
|
||||||
@@ -29,14 +18,9 @@ export interface EmployeeResponse {
|
|||||||
email: string;
|
email: string;
|
||||||
ssn: string;
|
ssn: string;
|
||||||
roles: RoleEnum[];
|
roles: RoleEnum[];
|
||||||
tjanster: TjanstResponse[];
|
tjanster: EmployeeTjanstResponse[];
|
||||||
allaUtforandeVerksamheter: boolean;
|
allaUtforandeVerksamheter: boolean;
|
||||||
utforandeVerksamheter: UtforandeVerksamhetResponse[];
|
utforandeVerksamheter: EmployeeUtforandeVerksamhetResponse[];
|
||||||
|
|
||||||
// Will be removed
|
|
||||||
tjansteKoder: string[];
|
|
||||||
utforandeVerksamhetIds: number[];
|
|
||||||
adressIds: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EmployeesDataResponse {
|
export interface EmployeesDataResponse {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
|
|
||||||
export interface UserInfoResponse {
|
export interface UserInfoResponse {
|
||||||
id: string;
|
id: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
roles: string[];
|
roles: RoleEnum[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export interface UtforandeAdressResponse {
|
||||||
|
id: number;
|
||||||
|
namn: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { UtforandeAdressResponse } from './utforande-adress.response.model';
|
||||||
|
|
||||||
|
export interface UtforandeVerksamhetResponse {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
adresser: UtforandeAdressResponse[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { EmployeeAdressResponse } from './api/employee-adress.response.model';
|
||||||
|
|
||||||
|
export interface EmployeeAdress {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapResponseToEmployeeAdress(data: EmployeeAdressResponse): EmployeeAdress {
|
||||||
|
const { id, namn } = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: namn,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { EmployeeTjanstResponse } from './api/employee-tjanst.response.model';
|
||||||
|
|
||||||
|
export interface EmployeeTjanst {
|
||||||
|
name: string;
|
||||||
|
tjansteKod: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapResponseToEmployeeTjanst(data: EmployeeTjanstResponse): EmployeeTjanst {
|
||||||
|
const { namn, tjansteKod } = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: namn,
|
||||||
|
tjansteKod,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { EmployeeUtforandeVerksamhetResponse } from './api/employee-utforande-verksamhet.response.model';
|
||||||
|
import { EmployeeAdress, mapResponseToEmployeeAdress } from './employee-adress.model';
|
||||||
|
|
||||||
|
export interface EmployeeUtforandeVerksamhet {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
allaAdresser: boolean;
|
||||||
|
adresser: EmployeeAdress[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapResponseToEmployeeUtforandeVerksamhet(
|
||||||
|
data: EmployeeUtforandeVerksamhetResponse
|
||||||
|
): EmployeeUtforandeVerksamhet {
|
||||||
|
const { id, namn, allaAdresser, adresser } = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: namn,
|
||||||
|
allaAdresser,
|
||||||
|
adresser: adresser.map(adress => mapResponseToEmployeeAdress(adress)),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,22 +1,14 @@
|
|||||||
import { RoleEnum } from '@msfa-enums/role.enum';
|
import { RoleEnum } from '@msfa-enums/role.enum';
|
||||||
import { EmployeeCompactResponse, EmployeeResponse } from './api/employee.response.model';
|
import { EmployeeCompactResponse, EmployeeResponse } from './api/employee.response.model';
|
||||||
|
import { EmployeeTjanst, mapResponseToEmployeeTjanst } from './employee-tjanst.model';
|
||||||
|
import {
|
||||||
|
EmployeeUtforandeVerksamhet,
|
||||||
|
mapResponseToEmployeeUtforandeVerksamhet,
|
||||||
|
} from './employee-utforande-verksamhet.model';
|
||||||
import { PaginationMeta } from './pagination-meta.model';
|
import { PaginationMeta } from './pagination-meta.model';
|
||||||
import { mapResponseToTjanst, Tjanst } from './tjanst.model';
|
|
||||||
|
|
||||||
const CURRENT_YEAR = new Date().getFullYear().toString().slice(2, 4);
|
const CURRENT_YEAR = new Date().getFullYear().toString().slice(2, 4);
|
||||||
|
|
||||||
interface UtforandeVerksamhet {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
allaAdresser: boolean;
|
|
||||||
adresser?: Adress[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Adress {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EmployeeCompact {
|
export interface EmployeeCompact {
|
||||||
id: string;
|
id: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
@@ -33,13 +25,9 @@ export interface Employee {
|
|||||||
email: string;
|
email: string;
|
||||||
ssn: string;
|
ssn: string;
|
||||||
roles: RoleEnum[];
|
roles: RoleEnum[];
|
||||||
tjanster: Tjanst[];
|
tjanster: EmployeeTjanst[];
|
||||||
allaUtforandeVerksamheter: boolean;
|
allaUtforandeVerksamheter: boolean;
|
||||||
utforandeVerksamheter: UtforandeVerksamhet[];
|
utforandeVerksamheter: EmployeeUtforandeVerksamhet[];
|
||||||
|
|
||||||
tjanstCodes: string[];
|
|
||||||
utforandeVerksamhetIds: number[];
|
|
||||||
utforandeAdressIds: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EmployeesData {
|
export interface EmployeesData {
|
||||||
@@ -81,9 +69,6 @@ export function mapResponseToEmployee(data: EmployeeResponse): Employee {
|
|||||||
tjanster,
|
tjanster,
|
||||||
allaUtforandeVerksamheter,
|
allaUtforandeVerksamheter,
|
||||||
utforandeVerksamheter,
|
utforandeVerksamheter,
|
||||||
tjansteKoder,
|
|
||||||
utforandeVerksamhetIds,
|
|
||||||
adressIds,
|
|
||||||
} = data;
|
} = data;
|
||||||
return {
|
return {
|
||||||
id: ciamUserId,
|
id: ciamUserId,
|
||||||
@@ -93,11 +78,10 @@ export function mapResponseToEmployee(data: EmployeeResponse): Employee {
|
|||||||
email,
|
email,
|
||||||
ssn: ssn ? mapResponseToSsn(ssn) : null,
|
ssn: ssn ? mapResponseToSsn(ssn) : null,
|
||||||
roles: roles || [],
|
roles: roles || [],
|
||||||
tjanster: tjanster?.map(tjanst => mapResponseToTjanst(tjanst)),
|
tjanster: tjanster?.map(tjanst => mapResponseToEmployeeTjanst(tjanst)),
|
||||||
allaUtforandeVerksamheter,
|
allaUtforandeVerksamheter,
|
||||||
utforandeVerksamheter,
|
utforandeVerksamheter: utforandeVerksamheter.map(utforandeVerksamhet =>
|
||||||
tjanstCodes: tjansteKoder || [],
|
mapResponseToEmployeeUtforandeVerksamhet(utforandeVerksamhet)
|
||||||
utforandeVerksamhetIds: utforandeVerksamhetIds || [],
|
),
|
||||||
utforandeAdressIds: adressIds || [],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Tjanst } from "./tjanst.model";
|
import { Tjanst } from './tjanst.model';
|
||||||
|
|
||||||
export interface FormTagData {
|
export interface FormTagData {
|
||||||
tjanstekod: string,
|
tjanstekod: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function mapTjanstToFormTag(tjanstData: Tjanst): FormTagData {
|
export function mapTjanstToFormTag(tjanstData: Tjanst): FormTagData {
|
||||||
const { name, code } = tjanstData;
|
const { name, code } = tjanstData;
|
||||||
return { tjanstekod: code, name}
|
return { tjanstekod: code, name };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { UserInfoResponse } from './api/user-info.response.model';
|
import { UserInfoResponse } from './api/user-info.response.model';
|
||||||
|
import { mapResponseToRoles, Role } from './role.model';
|
||||||
|
|
||||||
export interface UserInfo {
|
export interface UserInfo {
|
||||||
id: string;
|
id: string;
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
roles: string[];
|
roles: Role[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapResponseToUserInfo(data: UserInfoResponse): UserInfo {
|
export function mapResponseToUserInfo(data: UserInfoResponse): UserInfo {
|
||||||
@@ -16,6 +17,6 @@ export function mapResponseToUserInfo(data: UserInfoResponse): UserInfo {
|
|||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
fullName: `${firstName} ${lastName}`,
|
fullName: `${firstName} ${lastName}`,
|
||||||
roles,
|
roles: mapResponseToRoles(roles),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { OrganizationResponse } from './api/organization.response.model';
|
import { OrganizationResponse } from './api/organization.response.model';
|
||||||
import { UserInfoResponse } from './api/user-info.response.model';
|
import { UserInfoResponse } from './api/user-info.response.model';
|
||||||
import { mapResponseToOrganization, Organization } from './organization.model';
|
import { mapResponseToOrganization, Organization } from './organization.model';
|
||||||
|
import { mapResponseToRoles } from './role.model';
|
||||||
import { UserInfo } from './user-info.model';
|
import { UserInfo } from './user-info.model';
|
||||||
|
|
||||||
export interface User extends UserInfo {
|
export interface User extends UserInfo {
|
||||||
@@ -15,7 +16,7 @@ export function mapUserApiResponseToUser(userInfo: UserInfoResponse, organizatio
|
|||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
fullName: `${firstName} ${lastName}`,
|
fullName: `${firstName} ${lastName}`,
|
||||||
roles,
|
roles: mapResponseToRoles(roles),
|
||||||
organizations: organizations ? organizations.map(organization => mapResponseToOrganization(organization)) : [],
|
organizations: organizations ? organizations.map(organization => mapResponseToOrganization(organization)) : [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { UtforandeAdressResponse } from './api/utforande-adress.response.model';
|
||||||
|
|
||||||
|
export interface UtforandeAdress {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapResponseToUtforandeAdress(data: UtforandeAdressResponse): UtforandeAdress {
|
||||||
|
const { id, namn } = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: namn,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { UtforandeVerksamhetResponse } from './api/utforande-verksamhet.response.model';
|
||||||
|
import { mapResponseToUtforandeAdress, UtforandeAdress } from './utforande-adress.model';
|
||||||
|
|
||||||
|
export interface UtforandeVerksamhet {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
adresser: UtforandeAdress[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapResponseToUtforandeVerksamhet(data: UtforandeVerksamhetResponse): UtforandeVerksamhet {
|
||||||
|
const { id, name, adresser } = data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
adresser: adresser.map(adress => mapResponseToUtforandeAdress(adress)),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ import { Sort } from '@msfa-models/sort.model';
|
|||||||
import { ErrorService } from '@msfa-services/error.service';
|
import { ErrorService } from '@msfa-services/error.service';
|
||||||
import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
|
import { BehaviorSubject, combineLatest, Observable, of, throwError } from 'rxjs';
|
||||||
import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
import { catchError, distinctUntilChanged, filter, map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
import { TjanstService } from './tjanst.service';
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -48,11 +47,7 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
private _employeeToDelete$ = new BehaviorSubject<Employee>(null);
|
private _employeeToDelete$ = new BehaviorSubject<Employee>(null);
|
||||||
public employeeToDelete$: Observable<Employee> = this._employeeToDelete$.asObservable();
|
public employeeToDelete$: Observable<Employee> = this._employeeToDelete$.asObservable();
|
||||||
|
|
||||||
constructor(
|
constructor(private httpClient: HttpClient, private errorService: ErrorService) {
|
||||||
private httpClient: HttpClient,
|
|
||||||
private errorService: ErrorService,
|
|
||||||
private tjanstService: TjanstService
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
super.unsubscribeOnDestroy(
|
super.unsubscribeOnDestroy(
|
||||||
combineLatest([this._currentEmployeeId$, this._lastUpdatedEmployeeId$])
|
combineLatest([this._currentEmployeeId$, this._lastUpdatedEmployeeId$])
|
||||||
@@ -63,20 +58,7 @@ export class EmployeeService extends UnsubscribeDirective {
|
|||||||
!currLastUpdatedEmployeeId && prevEmployeeId === currEmployeeId
|
!currLastUpdatedEmployeeId && prevEmployeeId === currEmployeeId
|
||||||
),
|
),
|
||||||
switchMap(([currentEmployeeId]) =>
|
switchMap(([currentEmployeeId]) =>
|
||||||
combineLatest([this._fetchEmployee$(currentEmployeeId), this.tjanstService.tjanster$]).pipe(
|
this._fetchEmployee$(currentEmployeeId).pipe(filter(employee => !!employee))
|
||||||
filter(([employee, allTjanster]) => !!(employee && allTjanster?.length)),
|
|
||||||
map(([employee, allTjanster]) => {
|
|
||||||
const tjanster = [];
|
|
||||||
employee.tjanstCodes?.forEach(code => {
|
|
||||||
const currentTjanst = allTjanster.find(tjanst => tjanst.code === code);
|
|
||||||
|
|
||||||
if (currentTjanst) {
|
|
||||||
tjanster.push(currentTjanst);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return { ...employee, tjanster };
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subscribe(employee => {
|
.subscribe(employee => {
|
||||||
|
|||||||
@@ -17,23 +17,34 @@ import { AuthenticationService } from './authentication.service';
|
|||||||
})
|
})
|
||||||
export class UserService extends UnsubscribeDirective {
|
export class UserService extends UnsubscribeDirective {
|
||||||
private _apiBaseUrl = `${environment.api.url}/auth`;
|
private _apiBaseUrl = `${environment.api.url}/auth`;
|
||||||
|
private _isLoggedIn$: Observable<boolean> = this.authenticationService.isLoggedIn$;
|
||||||
|
private _organizations$ = new BehaviorSubject<Organization[]>(null);
|
||||||
|
public organizations$: Observable<Organization[]> = this._organizations$.asObservable();
|
||||||
private _user$ = new BehaviorSubject<User>(null);
|
private _user$ = new BehaviorSubject<User>(null);
|
||||||
public user$: Observable<User> = this._user$.asObservable();
|
public user$: Observable<User> = this._user$.asObservable();
|
||||||
private _selectedOrganizationNumber$ = new BehaviorSubject<string>(null);
|
private _selectedOrganizationNumber$ = new BehaviorSubject<string>(null);
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient, private authenticationService: AuthenticationService) {
|
constructor(private httpClient: HttpClient, private authenticationService: AuthenticationService) {
|
||||||
super();
|
super();
|
||||||
|
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
|
||||||
super.unsubscribeOnDestroy(
|
super.unsubscribeOnDestroy(
|
||||||
this.authenticationService.isLoggedIn$
|
this._isLoggedIn$
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(loggedIn => !!loggedIn),
|
filter(loggedIn => !!loggedIn),
|
||||||
switchMap(() => combineLatest([this._fetchUserInfo$(), this._fetchOrganizations$()]))
|
switchMap(() => this._fetchOrganizations$())
|
||||||
)
|
)
|
||||||
.subscribe(([userInfo, organizations]) => {
|
.subscribe(organizations => {
|
||||||
this._user$.next({ ...userInfo, organizations });
|
this._organizations$.next(organizations);
|
||||||
|
}),
|
||||||
|
combineLatest([this._isLoggedIn$, this.selectedOrganization$])
|
||||||
|
.pipe(
|
||||||
|
filter(([loggedIn, selectedOrganization]) => !!(loggedIn && selectedOrganization)),
|
||||||
|
switchMap(() => this._fetchUserInfo$())
|
||||||
|
)
|
||||||
|
.subscribe(userInfo => {
|
||||||
|
this._user$.next({ ...userInfo, organizations: this._organizations$.value });
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this._selectedOrganizationNumber$.next(this._selectedOrganizationNumber);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fetchOrganizations$(): Observable<Organization[]> {
|
private _fetchOrganizations$(): Observable<Organization[]> {
|
||||||
@@ -55,11 +66,11 @@ export class UserService extends UnsubscribeDirective {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get selectedOrganization$(): Observable<Organization | null> {
|
public get selectedOrganization$(): Observable<Organization | null> {
|
||||||
return combineLatest([this._selectedOrganizationNumber$, this._user$]).pipe(
|
return combineLatest([this._selectedOrganizationNumber$, this._organizations$]).pipe(
|
||||||
filter(([, user]) => !!user),
|
filter(([, organizations]) => !!organizations?.length),
|
||||||
map(([organizationNumber, user]) => {
|
map(([organizationNumber, organizations]) => {
|
||||||
return organizationNumber
|
return organizationNumber
|
||||||
? user.organizations.find(organization => organization.organizationNumber === organizationNumber)
|
? organizations.find(organization => organization.organizationNumber === organizationNumber)
|
||||||
: null;
|
: null;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,39 +2,34 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { environment } from '@msfa-environment';
|
import { environment } from '@msfa-environment';
|
||||||
import { Params } from '@msfa-models/api/params.model';
|
import { Params } from '@msfa-models/api/params.model';
|
||||||
|
import { UtforandeVerksamhetResponse } from '@msfa-models/api/utforande-verksamhet.response.model';
|
||||||
|
import { UtforandeAdress } from '@msfa-models/utforande-adress.model';
|
||||||
|
import { mapResponseToUtforandeVerksamhet, UtforandeVerksamhet } from '@msfa-models/utforande-verksamhet.model';
|
||||||
import {
|
import {
|
||||||
TreeNode,
|
TreeNode,
|
||||||
TreeNodesSelectorService,
|
TreeNodesSelectorService,
|
||||||
} from '@msfa-shared/components/tree-nodes-selector/services/tree-nodes-selector.service';
|
} from '@msfa-shared/components/tree-nodes-selector/services/tree-nodes-selector.service';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
export interface UtforandeVerksamhetAdress {
|
|
||||||
id: number;
|
|
||||||
namn: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UtforandeVerksamhet {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
adresser: Array<UtforandeVerksamhetAdress>;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class UtforandeVerksamheterService {
|
export class UtforandeVerksamheterService {
|
||||||
private readonly apiBaseUrl = `${environment.api.url}/utforandeverksamheter`;
|
private readonly _apiBaseUrl = `${environment.api.url}/utforandeverksamheter`;
|
||||||
|
|
||||||
constructor(private treeNodesSelectorService: TreeNodesSelectorService, private httpClient: HttpClient) {}
|
constructor(private treeNodesSelectorService: TreeNodesSelectorService, private httpClient: HttpClient) {}
|
||||||
|
|
||||||
getUtforandeVerksamheter(tjanstIds: Array<number>): Observable<Array<UtforandeVerksamhet>> {
|
fetchUtforandeVerksamheter$(tjanstIds: number[]): Observable<UtforandeVerksamhet[]> {
|
||||||
if (!tjanstIds.length) {
|
if (!tjanstIds.length) {
|
||||||
return of<Array<UtforandeVerksamhet>>([]);
|
return of<UtforandeVerksamhet[]>([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const params: Params = { tjansteIds: tjanstIds.map(tjanstId => tjanstId.toString()) };
|
const params: Params = { tjansteIds: tjanstIds.map(tjanstId => tjanstId.toString()) };
|
||||||
|
|
||||||
return this.httpClient.get<Array<UtforandeVerksamhet>>(`${this.apiBaseUrl}`, { params });
|
return this.httpClient
|
||||||
|
.get<{ data: UtforandeVerksamhetResponse[] }>(`${this._apiBaseUrl}`, { params })
|
||||||
|
.pipe(map(({ data }) => data.map(uv => mapResponseToUtforandeVerksamhet(uv))));
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedAdressIdsFromTreeNode(treeNode: TreeNode): number[] {
|
getSelectedAdressIdsFromTreeNode(treeNode: TreeNode): number[] {
|
||||||
@@ -43,7 +38,7 @@ export class UtforandeVerksamheterService {
|
|||||||
return selectedUtforandeVerksamheter.map(uv => uv.adresser.map(adress => adress.id)).flat();
|
return selectedUtforandeVerksamheter.map(uv => uv.adresser.map(adress => adress.id)).flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
getTreeNodeDataFromUtforandeVerksamheter(utforandeVerksamhetList: Array<UtforandeVerksamhet>): TreeNode | null {
|
getTreeNodeDataFromUtforandeVerksamheter(utforandeVerksamhetList: UtforandeVerksamhet[]): TreeNode | null {
|
||||||
let treeNode: TreeNode | null = null;
|
let treeNode: TreeNode | null = null;
|
||||||
|
|
||||||
if (!utforandeVerksamhetList || utforandeVerksamhetList.length === 0 || !Array.isArray(utforandeVerksamhetList)) {
|
if (!utforandeVerksamhetList || utforandeVerksamhetList.length === 0 || !Array.isArray(utforandeVerksamhetList)) {
|
||||||
@@ -67,7 +62,7 @@ export class UtforandeVerksamheterService {
|
|||||||
childItemType: 'Adresser',
|
childItemType: 'Adresser',
|
||||||
children: utforandeVerksamhet.adresser
|
children: utforandeVerksamhet.adresser
|
||||||
? utforandeVerksamhet.adresser.map(adress => {
|
? utforandeVerksamhet.adresser.map(adress => {
|
||||||
return { label: adress.namn, isSelected: false, value: adress };
|
return { label: adress.name, isSelected: false, value: adress };
|
||||||
})
|
})
|
||||||
: [],
|
: [],
|
||||||
};
|
};
|
||||||
@@ -80,8 +75,8 @@ export class UtforandeVerksamheterService {
|
|||||||
return treeNode;
|
return treeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedUtforandeVerksamheterFromTreeNode(treeNode: TreeNode): Array<UtforandeVerksamhet> {
|
getSelectedUtforandeVerksamheterFromTreeNode(treeNode: TreeNode): UtforandeVerksamhet[] {
|
||||||
let utforandeVerksamhetList: Array<UtforandeVerksamhet> = [];
|
let utforandeVerksamhetList: UtforandeVerksamhet[] = [];
|
||||||
|
|
||||||
if (!treeNode || !treeNode.children) {
|
if (!treeNode || !treeNode.children) {
|
||||||
return utforandeVerksamhetList;
|
return utforandeVerksamhetList;
|
||||||
@@ -95,7 +90,7 @@ export class UtforandeVerksamheterService {
|
|||||||
name: originalUtforandeVerksamhet?.name,
|
name: originalUtforandeVerksamhet?.name,
|
||||||
id: originalUtforandeVerksamhet?.id,
|
id: originalUtforandeVerksamhet?.id,
|
||||||
adresser: utforandeVerksamhetNode.children.map(adressNode => {
|
adresser: utforandeVerksamhetNode.children.map(adressNode => {
|
||||||
return adressNode.value as UtforandeVerksamhetAdress;
|
return adressNode.value as UtforandeAdress;
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
return utforandeVerksamhet;
|
return utforandeVerksamhet;
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ import { TreeNode } from '@msfa-shared/components/tree-nodes-selector/services/t
|
|||||||
export class TreeNodeValidator {
|
export class TreeNodeValidator {
|
||||||
static IsValidTreeNode(
|
static IsValidTreeNode(
|
||||||
validationFn: (treeNode: TreeNode | null | undefined) => boolean,
|
validationFn: (treeNode: TreeNode | null | undefined) => boolean,
|
||||||
nameOfError: string
|
nameOfError: string,
|
||||||
|
allUtforandeVerksamheterFormControl: AbstractControl
|
||||||
): ValidatorFn {
|
): ValidatorFn {
|
||||||
return (control: AbstractControl): { [key: string]: unknown } => {
|
return (control: AbstractControl): { [key: string]: unknown } => {
|
||||||
const isValid = validationFn(control.value);
|
const isValid = validationFn(control.value);
|
||||||
|
|
||||||
const validationObj = {};
|
const validationObj = {};
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid || allUtforandeVerksamheterFormControl?.value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,46 @@
|
|||||||
@import './variables/colors';
|
@import '~@digi/core/dist/collection/components/_button/button/button.css';
|
||||||
|
|
||||||
//Button properties
|
@mixin msfa__button($type: 'primary') {
|
||||||
|
padding: var(--digi-button--padding);
|
||||||
$msfa-button--padding: var(--digi-button--padding);
|
border-radius: var(--digi-button--border-radius);
|
||||||
$msfa-button--margin: 0.5rem;
|
transition: background-color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||||
$msfa-button--border-radius: var(--digi-button--border-radius);
|
|
||||||
$msfa-button--transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
|
|
||||||
$msfa-button--border: var(--digi-button--border);
|
|
||||||
$msfa-button--text-decoration: none;
|
|
||||||
$msfa-button--font-weight: var(--digi-button--font-weight);
|
|
||||||
$msfa-button--font-font-size: var(--digi-button--font-size);
|
|
||||||
|
|
||||||
//A basic link button
|
|
||||||
|
|
||||||
@mixin msfa-button-template($backgroundcolor, $textcolor, $hovercolor) {
|
|
||||||
background: $backgroundcolor;
|
|
||||||
padding: $msfa-button--padding;
|
|
||||||
margin: $msfa-button--margin;
|
|
||||||
border-radius: $msfa-button--border-radius;
|
|
||||||
transition: $msfa-button--transition;
|
|
||||||
border: $msfa-button--border;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: $msfa-button--font-weight;
|
font-weight: var(--digi-button--font-weight);
|
||||||
font-size: $msfa-button--font-font-size;
|
font-size: var(--digi-button--font-size);
|
||||||
color: $textcolor;
|
width: var(--digi-button--width);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.3rem;
|
||||||
|
text-align: var(--digi-button--text-align);
|
||||||
|
border: var(--digi-button--border);
|
||||||
|
outline: var(--digi-button--outline);
|
||||||
|
border-color: var(--digi-button--border-color);
|
||||||
|
|
||||||
&:hover {
|
@if $type == 'secondary' {
|
||||||
background: $hovercolor;
|
background-color: var(--digi-button--background--secondary);
|
||||||
|
color: var(--digi-button--color--secondary);
|
||||||
|
} @else if $type == 'tertiary' {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--digi-button--color--tertiary);
|
||||||
|
border-width: 0;
|
||||||
|
} @else {
|
||||||
|
background-color: var(--digi-button--background);
|
||||||
|
color: var(--digi-button--color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
outline: var(--digi-button--outline--focus);
|
||||||
|
border-color: var(--digi-button--border-color--hover);
|
||||||
|
|
||||||
|
@if $type == 'secondary' {
|
||||||
|
background-color: var(--digi-button--background--secondary--hover);
|
||||||
|
color: var(--digi-button--color--secondary--hover);
|
||||||
|
} @else if $type == 'tertiary' {
|
||||||
|
color: var(--digi-button--color--tertiary--hover);
|
||||||
|
} @else {
|
||||||
|
background-color: var(--digi-button--background--hover);
|
||||||
|
color: var(--digi-button--color--hover);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
apps/mina-sidor-fa/src/styles/mixins/_link.scss
Normal file
30
apps/mina-sidor-fa/src/styles/mixins/_link.scss
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
@mixin msfa__link($ignore-visited: false) {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--digi--typography--color--link);
|
||||||
|
font-size: var(--digi--typography--font-size);
|
||||||
|
font-weight: var(--digi--typography--font-weight--semibold);
|
||||||
|
gap: var(--digi--layout--gutter--xs);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $ignore-visited {
|
||||||
|
&:visited {
|
||||||
|
color: var(--digi--typography--color--link);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--digi--typography--color--link--active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin msfa__button-as-link($ignore-visited: false) {
|
||||||
|
@include msfa__link($ignore-visited);
|
||||||
|
background-color: transparent;
|
||||||
|
border-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
@import '@digi/core/dist/digi/digi.css';
|
@import '@digi/core/dist/digi/digi.css';
|
||||||
@import 'mixins/a11y';
|
@import 'mixins/a11y';
|
||||||
@import 'mixins/icon';
|
@import 'mixins/icon';
|
||||||
|
@import 'mixins/link';
|
||||||
|
|
||||||
@keyframes spinning {
|
@keyframes spinning {
|
||||||
from {
|
from {
|
||||||
@@ -82,25 +83,10 @@ dl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
display: inline-flex;
|
@include msfa__link(false);
|
||||||
align-items: center;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: var(--digi--typography--font-weight--semibold);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--with-icon {
|
|
||||||
gap: var(--digi--layout--gutter--xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
&--ignore-visited:visited {
|
&--ignore-visited:visited {
|
||||||
color: var(--digi--typography--color--link);
|
@include msfa__link(true);
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--digi--typography--color--link--active);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
@import '~@digi/styles/src/ui/variables/ui__variables';
|
@import '~@digi/styles/src/ui/variables/ui__variables';
|
||||||
@import '~@digi/core/dist/collection/components/_button/button/button.css';
|
|
||||||
|
|
||||||
// AF DIGI Variables
|
// AF DIGI Variables
|
||||||
$digi--ui--color--primary-light: lighten($digi--ui--color--primary, 10%);
|
$digi--ui--color--primary-light: lighten($digi--ui--color--primary, 10%);
|
||||||
$digi--ui--color--primary: $digi--ui--color--stratos;
|
$digi--ui--color--primary: $digi--ui--color--stratos;
|
||||||
|
|
||||||
// Local variables
|
// Local variables
|
||||||
|
|
||||||
$msfa-button--background--primary: var(--digi-button--background);
|
|
||||||
$msfa-button--text--primary: var(--digi--typography--color--text--light);
|
|
||||||
$msfa-button--hover--primary: var(--digi-button--background--hover);
|
|
||||||
$msfa-button--background--secondary: var(--digi-button--background--secondary);
|
|
||||||
$msfa-button--text--secondary: var(--digi--ui--color--primary);
|
|
||||||
$msfa-button--hover--secondary: var(--digi-button--background--secondary--hover);
|
|
||||||
|
|||||||
Reference in New Issue
Block a user