feat(portal-frontend): implement worklist-list component
Table of WorklistService results with bucket/origin/search filters (FormsModule ngModel); row click navigates to legacy/:id or owned/:id based on origin, matching the placeholder's key scheme.
This commit is contained in:
@@ -1 +1,43 @@
|
||||
<p>worklist-list works!</p>
|
||||
<h2>Werkvoorraad</h2>
|
||||
|
||||
<div class="filters">
|
||||
<label>
|
||||
Bucket
|
||||
<input type="text" name="bucket" [(ngModel)]="bucket" (ngModelChange)="onFilterChange()" />
|
||||
</label>
|
||||
<label>
|
||||
Origin
|
||||
<input type="text" name="origin" [(ngModel)]="origin" (ngModelChange)="onFilterChange()" />
|
||||
</label>
|
||||
<label>
|
||||
Zoeken
|
||||
<input type="text" name="search" [(ngModel)]="search" (ngModelChange)="onFilterChange()" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Origin</th>
|
||||
<th>Referentie</th>
|
||||
<th>Naam</th>
|
||||
<th>BSN</th>
|
||||
<th>Ontvangen</th>
|
||||
<th>Uitkomst</th>
|
||||
<th>Processtatus</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@for (item of items(); track keyOf(item).id) {
|
||||
<tr data-testid="worklist-row" (click)="openDetail(item)">
|
||||
<td>{{ item.origin }}</td>
|
||||
<td>{{ keyOf(item).id }}</td>
|
||||
<td>{{ item.surname }}, {{ item.initials }}</td>
|
||||
<td>{{ item.bsn }}</td>
|
||||
<td>{{ item.receivedOn }}</td>
|
||||
<td>{{ item.assessmentOutcome ?? 'n.v.t.' }}</td>
|
||||
<td>{{ item.processStatus ?? 'n.v.t.' }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,22 +1,91 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Router, provideRouter } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { WorklistService } from '../worklist.service';
|
||||
import { WorklistItem, WorklistPage, WorklistQuery } from '../worklist.types';
|
||||
import { WorklistList } from './worklist-list';
|
||||
|
||||
const legacyItem: WorklistItem = {
|
||||
origin: 'Legacy',
|
||||
legacyAanvraagId: 1001,
|
||||
registrationApplicationId: null,
|
||||
surname: 'de Vries',
|
||||
initials: 'A.',
|
||||
bsn: '123456782',
|
||||
receivedOn: '2026-01-01',
|
||||
bucket: 'ToBeAssessed',
|
||||
assessmentOutcome: null,
|
||||
processStatus: null,
|
||||
};
|
||||
|
||||
const ownedItem: WorklistItem = {
|
||||
...legacyItem,
|
||||
origin: 'Owned',
|
||||
legacyAanvraagId: null,
|
||||
registrationApplicationId: '00000000-0000-0000-0000-000000000002',
|
||||
};
|
||||
|
||||
function pageOf(items: WorklistItem[]): WorklistPage {
|
||||
return { items, page: 1, pageSize: 10, totalCount: items.length };
|
||||
}
|
||||
|
||||
describe('WorklistList', () => {
|
||||
let component: WorklistList;
|
||||
let fixture: ComponentFixture<WorklistList>;
|
||||
let getWorklist: ReturnType<typeof vi.fn>;
|
||||
let router: Router;
|
||||
|
||||
function createComponent() {
|
||||
fixture = TestBed.createComponent(WorklistList);
|
||||
fixture.detectChanges();
|
||||
router = TestBed.inject(Router);
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
getWorklist = vi.fn().mockReturnValue(of(pageOf([legacyItem, ownedItem])));
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [WorklistList],
|
||||
providers: [provideRouter([]), { provide: WorklistService, useValue: { getWorklist } }],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WorklistList);
|
||||
component = fixture.componentInstance;
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
it('loads and renders the worklist on init', () => {
|
||||
createComponent();
|
||||
|
||||
expect(getWorklist).toHaveBeenCalledWith({ bucket: undefined, origin: undefined, search: undefined });
|
||||
const rows = fixture.nativeElement.querySelectorAll('[data-testid="worklist-row"]');
|
||||
expect(rows.length).toBe(2);
|
||||
});
|
||||
|
||||
it('given a legacy-origin row, navigates to legacy/:id on click', () => {
|
||||
createComponent();
|
||||
const navigate = vi.spyOn(router, 'navigate');
|
||||
|
||||
const rows = fixture.nativeElement.querySelectorAll('[data-testid="worklist-row"]');
|
||||
rows[0].click();
|
||||
|
||||
expect(navigate).toHaveBeenCalledWith(['legacy', 1001]);
|
||||
});
|
||||
|
||||
it('given an owned-origin row, navigates to owned/:id on click', () => {
|
||||
createComponent();
|
||||
const navigate = vi.spyOn(router, 'navigate');
|
||||
|
||||
const rows = fixture.nativeElement.querySelectorAll('[data-testid="worklist-row"]');
|
||||
rows[1].click();
|
||||
|
||||
expect(navigate).toHaveBeenCalledWith(['owned', '00000000-0000-0000-0000-000000000002']);
|
||||
});
|
||||
|
||||
it('reloads with the new query when a filter changes', () => {
|
||||
createComponent();
|
||||
getWorklist.mockClear();
|
||||
|
||||
fixture.componentInstance.bucket = 'ToBeAssessed';
|
||||
fixture.componentInstance.onFilterChange();
|
||||
|
||||
expect(getWorklist).toHaveBeenCalledWith({ bucket: 'ToBeAssessed', origin: undefined, search: undefined } satisfies WorklistQuery);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,51 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, inject, signal } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { WorklistService } from '../worklist.service';
|
||||
import { WorklistItem, WorklistQuery } from '../worklist.types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-worklist-list',
|
||||
imports: [],
|
||||
imports: [FormsModule],
|
||||
templateUrl: './worklist-list.html',
|
||||
styleUrl: './worklist-list.css',
|
||||
})
|
||||
export class WorklistList {}
|
||||
export class WorklistList {
|
||||
private readonly worklistService = inject(WorklistService);
|
||||
private readonly router = inject(Router);
|
||||
|
||||
readonly items = signal<WorklistItem[]>([]);
|
||||
|
||||
bucket = '';
|
||||
origin = '';
|
||||
search = '';
|
||||
|
||||
constructor() {
|
||||
this.load();
|
||||
}
|
||||
|
||||
onFilterChange(): void {
|
||||
this.load();
|
||||
}
|
||||
|
||||
keyOf(item: WorklistItem): { segment: 'legacy' | 'owned'; id: number | string } {
|
||||
return item.origin === 'Legacy'
|
||||
? { segment: 'legacy', id: item.legacyAanvraagId! }
|
||||
: { segment: 'owned', id: item.registrationApplicationId! };
|
||||
}
|
||||
|
||||
openDetail(item: WorklistItem): void {
|
||||
const { segment, id } = this.keyOf(item);
|
||||
this.router.navigate([segment, id]);
|
||||
}
|
||||
|
||||
private load(): void {
|
||||
const query: WorklistQuery = {
|
||||
bucket: this.bucket || undefined,
|
||||
origin: this.origin || undefined,
|
||||
search: this.search || undefined,
|
||||
};
|
||||
this.worklistService.getWorklist(query).subscribe((page) => this.items.set(page.items));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user