feat(fp): WP-09 — pure-logic closure: dates + missing command specs

Consolidate four hand-rolled nl-NL date formatters (tasks.ts, aanvraag-
block, letter-preview, aanvraag-view -- one more than the WP found) into
one shared/kernel/datum.ts::formatDatumNl, spec-pinned and empty-safe.
Add the two missing command specs CLAUDE.md's testing rule calls for:
draft-sync.spec.ts (debounce coalescing + trailing-call + submit Result
shape, via fake timers) and submit-change-request.spec.ts. Remove the
unused RemoteData.map3 (updating the three docs that mentioned it); the
variant input on confirmation.component.ts was already gone. Documents
both stale-WP-text corrections in the backlog file.

This closes out backlog Phase 1 (FP/DDD core, WP-05..09).
This commit is contained in:
eho
2026-07-03 22:02:50 +02:00
parent 0d623f90e8
commit 8078c499cb
15 changed files with 1743 additions and 1878 deletions
-14
View File
@@ -71,20 +71,6 @@ export function map2<E, A, B, R>(
return { tag: 'Success', value: f(a.value, b.value) };
}
/** Combine three sources (built on map2). */
export function map3<E, A, B, C, R>(
a: RemoteData<E, A>,
b: RemoteData<E, B>,
c: RemoteData<E, C>,
f: (a: A, b: B, c: C) => R,
): RemoteData<E, R> {
return map2(
map2(a, b, (x, y) => [x, y] as const),
c,
([x, y], z) => f(x, y, z),
);
}
/** Chain a second source that depends on the first one's value. */
export function andThen<E, A, B>(
rd: RemoteData<E, A>,
+22
View File
@@ -0,0 +1,22 @@
import { describe, it, expect } from 'vitest';
import { formatDatumNl } from './datum';
describe('formatDatumNl', () => {
it('formats a Date in long Dutch form', () => {
expect(formatDatumNl(new Date(2026, 6, 2))).toBe('2 juli 2026');
});
it('formats an ISO string the same way', () => {
expect(formatDatumNl('2026-07-02')).toBe('2 juli 2026');
});
it('is empty-safe: undefined, null, and empty string all yield the empty string', () => {
expect(formatDatumNl(undefined)).toBe('');
expect(formatDatumNl(null)).toBe('');
expect(formatDatumNl('')).toBe('');
});
it('returns empty for an unparseable string rather than "Invalid Date"', () => {
expect(formatDatumNl('not-a-date')).toBe('');
});
});
+16
View File
@@ -0,0 +1,16 @@
/**
* The one hand-written date formatter for pure TS (non-template) code — a domain
* rule or a `$localize` string can't reach for Angular's `DatePipe`, so this covers
* that gap. Templates use `DatePipe` (`| date: 'longDate'`) instead; don't add a
* second hand-rolled formatter for either case.
*/
export function formatDatumNl(d: Date | string | undefined | null): string {
if (!d) return '';
const date = typeof d === 'string' ? new Date(d) : d;
if (Number.isNaN(date.getTime())) return '';
return new Intl.DateTimeFormat('nl-NL', {
day: 'numeric',
month: 'long',
year: 'numeric',
}).format(date);
}