feat(diary): replace numbered pager with infinite scroll - #2063
Conversation
Fixes #2060 Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com>
noUncheckedIndexedAccess makes CSS-module index access string | undefined; non-null assert to match the existing DateRangePicker.test.tsx pattern for toHaveClass(styles.<key>). Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
…activation E2E test from observer race Adds the missing German infiniteScroll.* keys (and removes the stale pagination block) in client/src/i18n/de/diary.json to restore i18n parity, resolving the Quality Gates Jest failure. Also stubs IntersectionObserver in the keyboard-only "Load more" E2E test so its own click/keypress activation path is isolated from the auto-scroll trigger, which could otherwise race and unmount the button before the test's focus assertion ran. Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com>
|
[ux-designer] Reviewed the diff against Token adherence — Clean. Every color/spacing/font-size/radius value in Dark mode — All colors route through semantic (Layer 2) tokens ( Component reuse — Correctly built as new shared artifacts in the right locations ( Interactive states / accessibility — Native Animation — The button's inline Responsive — Mobile media query widens the button to 100% and sets No blocking findings. Verdict: APPROVED |
|
[ux-designer] Note: could not use Verdict: APPROVED |
|
VERDICT: REQUEST_CHANGES [product-owner] Round 1 requirements review of PR #2063 against #2060 (23 ACs / 7 UAT scenarios), #2061 and #2062.
What I verifiedThe replace-don't-repair decision is executed faithfully and most of the acceptance record is met. Confirmed against the diff and the branch head:
Findings — all
|
|
[product-architect] Verdict: CHANGES REQUESTEDThe architecture of this rework is right, and the #2061 fix is genuinely well done — the epoch counter is the correct mechanism and its regression test is a real revert test (it asserts Verified
Blocking1. HIGH —
|
…ops, pluralized announcements - useInfiniteScroll: epoch-gated batch metadata, fetchSequence/lastBatchCount reset on resetKey change - InfiniteScrollFooter: reusable props instead of diary-specific assumptions, removed duplicate loading indicator, dropped dead hasMore prop - DiaryPage: wiring updates for the above - i18n: singular/plural announcement keys in en/de diary.json (screen-reader announcements now grammatically correct for count === 1) - e2e: updated sentinel/footer testids in DiaryPage POM to match Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com>
|
[ux-designer] Re-confirming approval on the removal of the standalone `.statusRow` (item 2/6 of my original spec on #2060). I reviewed
Verdict: approved — the single inline indicator inside the button is sufficient, and this is a net improvement over my original spec. |
|
VERDICT: APPROVE [product-owner] Round 2 requirements review of PR #2063 against #2060 (23 ACs / 7 UAT scenarios), #2061 and #2062. Verified at head
All five round-1 findings are closed1 (High) — superseded fetch overwriting the header total: fixed at the right layer. The demanded test exists and is a real revert test, not a vacuous one. 2 (Medium) — footer hardcoded to the diary domain: fixed via the props route. 3 (Medium) — AC5 double loading affordance: fixed. The standalone 4 (Low) — pluralisation: fixed, following the local convention. Six 5 (Low) — dead Round-2 regression checkI re-checked what the round-2 edits made newly reachable:
I consider my enumeration of this PR complete across both rounds — nothing further is being held back. Deferred, filed as #2065 — Medium, not blockingLoad more loses keyboard focus while its batch loads. The button carries Deferred rather than fixed in session for two reasons: it is not a round-2 regression (the attribute was present at round 1 and I did not flag it), and the remedy — AC16's letter is met — one keyboard activation loads one batch — which is why this does not block. Merge instruction (unchanged from round 1)
ScopeStill clean. No page-size selector, |
|
VERDICT: REQUEST_CHANGES [product-architect] Verdict: CHANGES REQUESTED (round 2)All four round-1 blockers are genuinely resolved, and two of the three fixes are better than what I proposed. Both findings below are mechanical and contained to this PR's files — fix-in-session, one commit. Flagging up front that this is round 2 of the capped review loop: if the orchestrator judges the cap binding, item 1 is the one that must not be deferred silently, and item 2 can go to a filed issue. Round-1 blockers — verified fixed1 (HIGH, unguarded side effects in The JSDoc obligation is met — Test coverage is real, not decorative. 2 (MEDIUM, diary-hardcoded namespace/testids) — fixed via a different route, also acceptable. Rather than relocating keys to 3 (MEDIUM, 4 (LOW, dead Also verified: no orphan references to the removed Blocking1. MEDIUM — hand-rolled
|
…registration - i18n: renamed the six infiniteScroll announcement keys from hand-rolled Singular/Plural suffixes to native i18next _one/_other pluralization (en/de diary.json), matching the existing convention in dashboard.json and budget.json - DiaryPage: removed the manual suffix-construction logic; announcement effect now calls the base key directly and lets i18next select the plural form from `count` - CLAUDE.md: registered InfiniteScrollFooter/useInfiniteScroll in the Component Reuse Policy shared-components list Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com>
|
[product-architect] Round 3 re-review (final) — PR #2063 / issue #2060 Both round-2 findings verified as genuinely resolved, not merely claimed. Checked at commit Finding 1 (round 2, MEDIUM) — hand-rolled
|
…2063 round 3 Co-Authored-By: Claude product-architect <noreply@anthropic.com>
…2060) review Captures agent-memory updates written during this session's review rounds for the diary infinite-scroll PR that were left uncommitted in the shared worktree: - e2e-test-engineer: testIdPrefix genericization discrepancy, statusRow removal verification - product-architect: epoch-guard-doesn't-protect-consumer-writes pattern, shared-component-in-shared-directory-isn't-automatically-shareable, _one/_other vs hand-rolled suffix ruling - product-owner: PR #2063 review log (R1 REQUEST_CHANGES, R2 APPROVED), diary cluster index update, deferred issue #2065 cross-reference - translator: _one/_other rename, German strong-declension pattern for the split announcement keys, key-parity verification method - ux-designer: reversed own statusRow spec after PO/architect findings, lesson on checking for an existing page-level live region before specifying a component-local one These are documentation-only agent-memory files, not production code — no Delegation Enforcement trailers required, but crediting the agents whose memory this is per CLAUDE.md's attribution convention. Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude product-owner <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com> Co-Authored-By: Claude ux-designer <noreply@anthropic.com>
|
🎉 This PR is included in version 2.15.0-beta.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
DiaryPage.tsxlistedsearchParamsin its dependency array —useSearchParams()re-identifies that object on every URL change, so the effect unconditionally re-rannewParams.set('page', '1')and clobbered every pager click back to page 1.?page=URL param are removed entirely (no page-size selector). In their place, a new shareduseInfiniteScrollhook (IntersectionObserver-based, dedupe-safe,idle/loading/error/donestate machine) and a new sharedInfiniteScrollFooterpresentational component drive scroll-triggered batch loading on the diary entries list.infiniteScroll.*inclient/src/i18n/en/diary.json; the now-removedpagination.*keys were deleted.Fixes #2060
Fixes #2061
Fixes #2062
Notes
dev-team-leadreview twice: round 1 returnedCHANGES_REQUIRED(3 fixes, including the stalesearchParamsdependency finding), round 2 returnedAPPROVEDafter verification.Test plan
useInfiniteScroll.test.tsx,InfiniteScrollFooter.test.tsx, updatedDiaryPage.test.tsxe2e/pages/DiaryPage.tsPOM ande2e/tests/diary/diary-list.spec.tsCo-Authored-By: Claude dev-team-lead noreply@anthropic.com
Co-Authored-By: Claude frontend-developer noreply@anthropic.com
Co-Authored-By: Claude qa-integration-tester noreply@anthropic.com
Co-Authored-By: Claude e2e-test-engineer noreply@anthropic.com