Added better staff mock data and updated several pages
This commit is contained in:
committed by
Erik Tiekstra
parent
cee9168c3c
commit
1ee6ca9251
@@ -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('/'),
|
||||
})),
|
||||
]);
|
||||
|
||||
10
apps/dafa-web/src/app/data/constants/navigation.ts
Normal file
10
apps/dafa-web/src/app/data/constants/navigation.ts
Normal 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',
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
56
apps/dafa-web/src/app/services/api/staff.service.ts
Normal file
56
apps/dafa-web/src/app/services/api/staff.service.ts
Normal 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) {}
|
||||
}
|
||||
@@ -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)}`;
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-weight: var(--digi--typography--font-weight);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
|
||||
Reference in New Issue
Block a user