Added better staff mock data and updated several pages

This commit is contained in:
Erik Tiekstra
2021-04-09 08:48:22 +02:00
committed by Erik Tiekstra
parent cee9168c3c
commit 1ee6ca9251
27 changed files with 385 additions and 56 deletions

View File

@@ -2,6 +2,7 @@ import { NavigationBreadcrumbsItem } from '@af/digi-ng/_navigation/navigation-br
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { NavigationEnd, Router } from '@angular/router';
import { User } from '@dafa-models/user.model';
import { mapPathToPageName } from '@dafa-utils/map-path-to-page-name.util';
import { BehaviorSubject } from 'rxjs';
import { filter } from 'rxjs/operators';
import { UnsubscribeDirective } from './directives/unsubscribe.directive';
@@ -41,7 +42,7 @@ export class AppComponent extends UnsubscribeDirective {
...[...paths]
.filter(path => !!path)
.map(path => ({
text: `${path.charAt(0).toUpperCase()}${path.slice(1)}`,
text: mapPathToPageName(path),
routerLink: paths.slice(0, paths.length - 1).join('/'),
})),
]);

View File

@@ -0,0 +1,10 @@
export const Navigation = {
administration: 'Administration',
'skapa-konto': 'Skapa nytt konto',
personal: 'Personal',
'mina-deltagare': 'Mina deltagare',
avrop: 'Avrop',
meddelanden: 'Meddelanden',
statistik: 'Statistik',
installningar: 'Inställningar',
};

View File

@@ -2,7 +2,7 @@ import { ParticipantStatus } from '@dafa-enums/participant-status.enum';
import { Service } from '@dafa-enums/service.enum';
export interface Participant {
id: number;
id: string;
firstName: string;
lastName: string;
status: ParticipantStatus;

View File

@@ -1,6 +1,7 @@
import { Participant } from './participant.model';
import { Staff } from './staff.model';
export interface SortBy {
key: keyof Participant;
key: keyof Participant | keyof Staff;
reverse: boolean;
}

View File

@@ -1,16 +1,10 @@
import { ParticipantStatus } from '@dafa-enums/participant-status.enum';
import { Service } from '@dafa-enums/service.enum';
export interface Participant {
id: number;
export interface Staff {
id: string;
staffId: string;
firstName: string;
lastName: string;
status: ParticipantStatus;
nextStep: string;
service: Service;
errandNumber: number;
startDate: Date;
endDate: Date;
handleBefore: Date;
kommun: string;
active: boolean;
service: string;
fullName?: string;
}

View File

@@ -11,7 +11,7 @@ const routes: Routes = [
},
{
path: 'personal',
loadChildren: () => import('./pages/staff-list/staff-list.module').then(m => m.StaffListModule),
loadChildren: () => import('./pages/staff/staff.module').then(m => m.StaffModule),
},
{
path: 'skapa-konto',

View File

@@ -1,14 +0,0 @@
<section class="staff-list">
<digi-typography>
<h1>Personallista</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam magna neque, interdum vel massa eget, condimentum
rutrum velit. Sed vitae ullamcorper sem. Aliquam malesuada nunc sed purus mollis scelerisque. Curabitur bibendum
leo quis ante porttitor tincidunt. Nam tincidunt imperdiet tortor eu suscipit. Maecenas ut dui est.
</p>
<digi-button>
Skapa nytt konto
<dafa-icon [icon]="iconType.PLUS"></dafa-icon>
</digi-button>
</digi-typography>
</section>

View File

@@ -1,12 +0,0 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IconType } from '@dafa-enums/icon-type.enum';
@Component({
selector: 'dafa-staff-list',
templateUrl: './staff-list.component.html',
styleUrls: ['./staff-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StaffListComponent {
iconType = IconType;
}

View File

@@ -0,0 +1,73 @@
<digi-ng-table>
<table>
<thead>
<tr>
<th scope="col" class="staff-list__column-head">
<button class="staff-list__sort-button" (click)="handleSort('fullName')">
Namn
<ng-container *ngIf="sortBy?.key === 'fullName'">
<digi-icon-caret-up class="staff-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="staff-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container>
</button>
</th>
<th scope="col" class="staff-list__column-head">
<button class="staff-list__sort-button" (click)="handleSort('staffId')">
Personal-ID
<ng-container *ngIf="sortBy?.key === 'staffId'">
<digi-icon-caret-up class="staff-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="staff-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container>
</button>
</th>
<th scope="col" class="staff-list__column-head">
<button class="staff-list__sort-button" (click)="handleSort('kommun')">
Kommun
<ng-container *ngIf="sortBy?.key === 'kommun'">
<digi-icon-caret-up class="staff-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="staff-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container>
</button>
</th>
<th scope="col" class="staff-list__column-head">
<button class="staff-list__sort-button" (click)="handleSort('active')">
Aktiv i tjänst
<ng-container *ngIf="sortBy?.key === 'active'">
<digi-icon-caret-up class="staff-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="staff-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container>
</button>
</th>
<th scope="col" class="staff-list__column-head">
<button class="staff-list__sort-button" (click)="handleSort('service')">
Tjänst
<ng-container *ngIf="sortBy?.key === 'service'">
<digi-icon-caret-up class="staff-list__sort-icon" *ngIf="!sortBy.reverse"></digi-icon-caret-up>
<digi-icon-caret-down class="staff-list__sort-icon" *ngIf="sortBy.reverse"></digi-icon-caret-down>
</ng-container>
</button>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let staff of pagedStaff">
<th scope="row">{{ staff.firstName }} {{ staff.lastName }}</th>
<td>{{ staff.staffId }}</td>
<td>{{ staff.kommun }}</td>
<td>{{ staff.active ? 'Ja' : 'Nej' }}</td>
<td>{{ staff.service }}</td>
</tr>
</tbody>
</table>
</digi-ng-table>
<digi-navigation-pagination
*ngIf="staff.length > pagedStaff.length"
class="staff-list__pagination"
[afTotalPages]="totalPages"
[afCurrentResultStart]="currentResultStart"
[afCurrentResultEnd]="currentResultEnd"
[afTotalResults]="staff.length"
(afOnPageChange)="handlePagination($event.detail)"
af-result-name="deltagare"
></digi-navigation-pagination>

View File

@@ -0,0 +1,35 @@
@import 'variables/gutters';
.staff-list {
&__column-head {
padding: 0;
}
&__sort-button {
position: relative;
background-color: transparent;
border-width: 0;
width: 100%;
text-align: left;
padding: var(--digi--layout--gutter--s) $digi--layout--gutter--l var(--digi--layout--gutter--s)
var(--digi--layout--gutter);
margin: 0;
font-size: inherit;
font-weight: inherit;
display: flex;
align-items: center;
gap: var(--digi--layout--gutter);
cursor: pointer;
}
&__sort-icon {
position: absolute;
display: inline-flex;
right: 0.5rem;
}
&__pagination {
display: block;
margin-top: var(--digi--layout--gutter);
}
}

View File

@@ -0,0 +1,27 @@
import { DigiNgTableModule } from '@af/digi-ng/_table/table';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { StaffListComponent } from './staff-list.component';
describe('StaffListComponent', () => {
let component: StaffListComponent;
let fixture: ComponentFixture<StaffListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [StaffListComponent],
imports: [RouterTestingModule, DigiNgTableModule],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(StaffListComponent);
component = fixture.componentInstance;
component.staff = [];
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,47 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { SortBy } from '@dafa-models/sort-by.model';
import { Staff } from '@dafa-models/staff.model';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'dafa-staff-list',
templateUrl: './staff-list.component.html',
styleUrls: ['./staff-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StaffListComponent {
@Input() staff: Staff[];
@Input() sortBy: SortBy | null;
@Output() sorted = new EventEmitter<keyof Staff>();
private _currentPage$ = new BehaviorSubject<number>(1);
private _staffPerPage = 10;
get currentPage(): number {
return this._currentPage$.getValue();
}
get totalPages(): number {
return Math.ceil(this.staff.length / this._staffPerPage);
}
get pagedStaff(): Staff[] {
return [...this.staff].slice(this.currentResultStart - 1, this.currentResultEnd - 1);
}
get currentResultStart(): number {
return (this.currentPage - 1) * this._staffPerPage + 1;
}
get currentResultEnd(): number {
return this.currentResultStart + this._staffPerPage;
}
handleSort(key: keyof Staff): void {
this.sorted.emit(key);
}
handlePagination(page: number): void {
this._currentPage$.next(page);
}
}

View File

@@ -1,12 +1,12 @@
import { DigiNgTableModule } from '@af/digi-ng/_table/table';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { IconModule } from '@dafa-shared/components/icon/icon.module';
import { StaffListComponent } from './staff-list.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [StaffListComponent],
imports: [CommonModule, RouterModule.forChild([{ path: '', component: StaffListComponent }]), IconModule],
imports: [CommonModule, DigiNgTableModule],
exports: [StaffListComponent],
})
export class StaffListModule {}

View File

@@ -0,0 +1,31 @@
<section class="staff">
<digi-typography>
<h1>Personal</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam magna neque, interdum vel massa eget, condimentum
rutrum velit. Sed vitae ullamcorper sem. Aliquam malesuada nunc sed purus mollis scelerisque. Curabitur bibendum
leo quis ante porttitor tincidunt. Nam tincidunt imperdiet tortor eu suscipit. Maecenas ut dui est.
</p>
<div class="staff__cta-wrapper">
<digi-ng-link-internal afText="Skapa nytt konto" afRoute="/administration/skapa-konto"></digi-ng-link-internal>
</div>
<h2>Personallista</h2>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum, officia perferendis? Excepturi animi rem culpa
facere, laboriosam vel, quia eos eligendi, cupiditate doloribus aspernatur unde nihil iste earum corrupti illo.
</p>
<dafa-staff-list
*ngIf="filteredStaff$ | async as staff; else loadingRef"
[staff]="staff"
[sortBy]="staffSortBy$ | async"
(sorted)="handleStaffSort($event)"
></dafa-staff-list>
</digi-typography>
<ng-template #loadingRef>
<digi-ng-skeleton-base [afCount]="3" afText="Laddar personal"></digi-ng-skeleton-base>
</ng-template>
</section>

View File

@@ -0,0 +1,11 @@
.staff {
&__cta-wrapper {
margin-top: var(--digi--layout--gutter);
}
&__link {
display: inline-flex;
align-items: center;
gap: var(--digi--layout--gutter--s);
}
}

View File

@@ -1,22 +1,22 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { StaffListComponent } from './staff-list.component';
import { StaffComponent } from './staff.component';
describe('StaffListComponent', () => {
let component: StaffListComponent;
let fixture: ComponentFixture<StaffListComponent>;
describe('StaffComponent', () => {
let component: StaffComponent;
let fixture: ComponentFixture<StaffComponent>;
beforeEach(
waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [StaffListComponent],
declarations: [StaffComponent],
imports: [RouterTestingModule],
}).compileComponents();
})
);
beforeEach(() => {
fixture = TestBed.createComponent(StaffListComponent);
fixture = TestBed.createComponent(StaffComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

View File

@@ -0,0 +1,37 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IconType } from '@dafa-enums/icon-type.enum';
import { SortBy } from '@dafa-models/sort-by.model';
import { Staff } from '@dafa-models/staff.model';
import { StaffService } from '@dafa-services/api/staff.service';
import { BehaviorSubject, Observable } from 'rxjs';
@Component({
selector: 'dafa-staff',
templateUrl: './staff.component.html',
styleUrls: ['./staff.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class StaffComponent {
private _searchValue$ = new BehaviorSubject<string>('');
filteredStaff$: Observable<Staff[]> = this.staffService.filteredStaff$;
staffSortBy$: Observable<SortBy | null> = this.staffService.staffSortBy$;
iconType = IconType;
constructor(private staffService: StaffService) {}
get searchValue(): string {
return this._searchValue$.getValue();
}
handleSearchSubmit(): void {
this.staffService.setSearchFilter(this.searchValue);
}
handleSearchInput($event: CustomEvent): void {
this._searchValue$.next($event.detail.target.value);
}
handleStaffSort(key: keyof Staff): void {
this.staffService.setStaffSortKey(key);
}
}

View File

@@ -0,0 +1,20 @@
import { DigiNgLinkInternalModule } from '@af/digi-ng/_link/link-internal';
import { DigiNgSkeletonBaseModule } from '@af/digi-ng/_skeleton/skeleton-base';
import { CommonModule } from '@angular/common';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { StaffListModule } from './components/staff-list/staff-list.module';
import { StaffComponent } from './staff.component';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [StaffComponent],
imports: [
CommonModule,
RouterModule.forChild([{ path: '', component: StaffComponent }]),
DigiNgLinkInternalModule,
DigiNgSkeletonBaseModule,
StaffListModule,
],
})
export class StaffModule {}

View File

@@ -0,0 +1,56 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@dafa-environment';
import { SortBy } from '@dafa-models/sort-by.model';
import { Staff } from '@dafa-models/staff.model';
import { sort } from '@dafa-utils/sort.util';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
function filterStaff(staff: Staff[], searchFilter: string): Staff[] {
return staff.filter(person => {
const searchValueExistsInName = person.fullName.toLowerCase().includes(searchFilter.toLowerCase());
return searchValueExistsInName;
});
}
@Injectable({
providedIn: 'root',
})
export class StaffService {
private _allStaff$: Observable<Staff[]> = this.httpClient.get<Staff[]>(`${environment.apiBase}/staff`).pipe(
map(staff =>
staff.map(person => ({
...person,
fullName: `${person.firstName} ${person.lastName}`,
}))
)
);
private _staffSortBy$ = new BehaviorSubject<SortBy | null>({ key: 'fullName', reverse: false });
public staffSortBy$: Observable<SortBy> = this._staffSortBy$.asObservable();
private _searchFilter$ = new BehaviorSubject<string>('');
public searchFilter$: Observable<string> = this._searchFilter$.asObservable();
private _filteredStaff$: Observable<Staff[]> = combineLatest([this._allStaff$, this._searchFilter$]).pipe(
map(([staff, searchFilter]) => filterStaff(staff, searchFilter))
);
public filteredStaff$: Observable<Staff[]> = combineLatest([this._filteredStaff$, this._staffSortBy$]).pipe(
map(([staff, sortBy]) => {
return sortBy ? sort(staff, sortBy) : staff;
})
);
public setSearchFilter(value: string) {
this._searchFilter$.next(value);
}
public setStaffSortKey(key: keyof Staff) {
const currentSortBy = this._staffSortBy$.getValue();
const reverse = currentSortBy?.key === key ? !currentSortBy.reverse : false;
this._staffSortBy$.next({ key, reverse });
}
constructor(private httpClient: HttpClient) {}
}

View File

@@ -0,0 +1,5 @@
import { Navigation } from '@dafa-constants/navigation';
export function mapPathToPageName(path: string): string {
return Navigation[path] || `${path.charAt(0).toUpperCase()}${path.slice(1)}`;
}

View File

@@ -15,6 +15,7 @@
body {
margin: 0;
font-weight: var(--digi--typography--font-weight);
overflow: hidden;
}
button {