fix(reports): improve report PDF UX — paragraph breaks, inline meta, inline notes, column toggles - #1959
Conversation
2175ab2 to
23c3537
Compare
…inline notes, column toggles - AI cover letter body now splits on double newlines into separate pdfmake content blocks, preserving paragraph breaks from the LLM response - Area and attachments count moved inline into the usage cell (grey, same font size) instead of a separate stack/sub-row - Split (†) and deposit-reduced (‡) footnotes replaced with inline grey labels in the allocated amount cell, matching the existing deposit pattern; allocatedMarkers field removed from ReportContentRow in favour of boolean isSplit / isDepositReduced flags - ReportContentEditor gains per-column visibility toggles (local state, no persistence); attachments column removed — content is now inline under usage Co-Authored-By: Claude frontend-developer <noreply@anthropic.com>
…ontent The inline-meta change appended areaText/attachmentsNote as one unchunked run on the usage cell. With table.dontBreakRows, pdfmake does not paginate an over-tall row — it silently discards the overflow. This reintroduced the #1929 round-4 content-loss regression: page count saturated at 2 and went non-monotonic (3 -> 2 as content grew) while rendered line count kept rising linearly, so ~7 pages of measured content were thrown away. Reachable from ordinary data: attachmentsNote has no maxLength and areaText is unbounded across leaf areas. packUsageCellRows() now bounds the whole rendered cell stream rather than usageText alone, splitting via the existing splitIntoPageSafeChunks(). Page count is monotonic again and identical whichever channel carries the text. Where the cell fits one page-safe row — the dominant case — output is byte-identical. The grey suffix now lands on the last chunk row rather than the first: pinning it to row 0 rendered meta mid-prose with more usage below it, and forced the usage chunk boundary to shrink. Also updates the tests that encoded the unbounded model. Two of them passed while the PDF lost content, because they inspected the pdfmake content tree rather than rendered output; the guard is now framed as rendered-output-only and asserts that the same text costs the same number of pages whichever channel carries it. Adds coverage for packUsageCellRows (none existed), the inline split/deposit labels, and the column toggles. Removes the small-chunk constants, now dead. E2E: reportWizard scenarios updated for the renamed usage-meta class, the removed attachments field, and the footnote-to-inline-label change. German strings verified against the glossary; abzgl. Abschlag kept over the approved Abschlagszahlung because the compliant term measures 96pt against a 75pt column. 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>
23c3537 to
9eaa929
Compare
|
[product-architect] VERDICT: APPROVEDArchitecture review of the fix round on head I did not re-verify test coverage per your note. I did independently re-derive the packer's invariants. Independent verification of
|
| # | Finding | Severity | Disposition |
|---|---|---|---|
| 1 | Budget assertion is generic over runs but inputs are hand-enumerated — a new channel passes vacuously | Medium | Key-driven saturation; the answer to "what stops round 5" |
| 2 | Meta on last row | — | Sound. One legibility item for UAT |
| 3 | footnotes is a permanently-empty channel with 4 consumers |
Low | Follow-up issue |
| 3b | (abzgl. Abschlag) loses "claimed separately" |
Medium | Needs a PO ruling (with #6) |
| 4 | applyOverrides attachmentsNote is unreachable dead code |
Low | Follow-up issue |
| 5 | Column toggles are preview-only but read as report-composition | Medium | Label preview-only or drop before promotion; design-system verdict is ux-designer's |
| 6 | #1923 ACs reversed with no PO ruling; #1958 will misreport them | Medium | Two issue comments before #1958 promotes |
| 7 | packUsageCellRows hangs on maxChars <= 0 |
Low | Guard; matters only if #5 is ever plumbed |
| 8 | ADR-034 amendment + #1929 debt | Medium | Mine. Follow-up |
Recommend closing before #1958 promotes: 5 and 6. Both are cheap — a label and two issue comments — and both concern what a bank actually receives or what the record claims was delivered. Everything else is a follow-up issue, and 1 is the one I would prioritise, because it is what stops this from becoming round 5.
One theme worth naming, since findings 3, 4 and 5 are the same shape: a capability retained in code, disabled at the producer, and documented by comment instead of by type. Empty-but-handled footnotes, an override key with no producer, a toggle with no effect on the output. Each is individually harmless; collectively they are why this module keeps needing archaeology. Deleting rather than commenting is the cheaper long-run move.
…ound rule - Usage-cell height bound: bound what a cell RENDERS, never a source field - Generic-assertion / hand-enumerated-input asymmetry as the recurrence cause - Column-count constraint pinning MAX_SAFE_USAGE_CHUNK_CHARS to the 7-col shape - Owed ADR-034 corrections (minimum-bar rule #1, module table, override keys) - Two new recurring patterns: dead capability retained, AC reversal by polish issue Co-Authored-By: Claude product-architect <noreply@anthropic.com>
|
[ux-designer] Design review of #1959 (head Verdict: CHANGES_REQUIREDOne blocking finding, verified by real render. Everything else is non-blocking and listed below as follow-ups. Method. I did not judge this from the diff. I rendered the real, unmocked pipeline ( BLOCKING — the
|
- Footnote-to-inline-label reversal: labels kept, explanatory sentences restored as a report-level legend (#1965); supersession-comment policy for ACs on closed/released issues instead of rewriting them - Glossary: Abschlag approved as a measured-space short form; split's three German surface forms deliberately get no entry Co-Authored-By: Claude product-owner <noreply@anthropic.com>
- Multi-word inline labels split mid-phrase in narrow fixed-width PDF cells; an isolated width measurement cannot catch it. Verified U+00A0 fix. - Check that a new report/export editor control actually reaches the export pipeline; DataTableColumnSettings already exists for column toggles. - Compare preview-component styles against the exporter's constants. Co-Authored-By: Claude ux-designer <noreply@anthropic.com>
depositReducedInlineLabel was the only inline label in either locale with an internal space, so at 8pt in the narrow allocated-amount column it wrapped there — putting the opening and closing bracket on different lines. "EUR 4,000.00 (less" / "deposit)" in en, "(Teilbetrag) (abzgl." / "Abschlag)" in de, in a document sent to a bank. Both now use U+00A0, which has the same glyph advance, so no width or geometry constant moves. The earlier width analysis measured the label alone against the 75pt column. It always follows the formatted amount in the same run stream, so the combined run exceeds the column regardless and the label always wrapped: breakability was the operative variable, not width. Verified by rendering a plain-space control from the same harness and reproducing the break, then confirming it gone. Adds a locale-level invariant so this cannot regress silently: the two keys that exist solely as bracketed labels must contain no breaking whitespace, checked against every locale. attachmentType.deposit is held to a weaker single-word rule instead, because it is also comma-joined into flowing prose where a non-breaking space would be over-reach. Also guards packUsageCellRows and splitIntoPageSafeChunks against a non-positive budget. Both hung rather than failing loudly — the packer in its own loop, before it ever delegates — so guarding only the primitive would not have fixed it. Labels the ReportContentEditor column toggles preview-only: their state never leaves the component, but every sibling control does reach the PDF, so the label read as "choose the report's columns". Wiring is #1966. Glossary records Abschlag as a measured-space short form of Abschlagszahlung, with the 75pt budget as the reason, so a future compliance sweep cannot silently break the PDF. Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com> Co-Authored-By: Claude translator <noreply@anthropic.com>
|
🎉 This PR is included in version 2.13.0-beta.52 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 2.13.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
…tWizardPage to useReducer - **#1967** — Remove dead `attachmentsNote` override from `applyOverrides.ts` and `overrideKeys.ts`; field has been static read-only text since PR #1959, no UI path can produce its override key; adds regression test pinning the removal - **#1947** — Refactor `ReportWizardPage` from 38 `useState`/`useRef` hooks to a `useReducer` state machine in `wizardReducer.ts`; staleness now enforced via opaque request-id tokens (M1/M2 fixes); named tier types with explicit factory return-type annotations enforce AC4 at compile time; 57 unit tests at 100% coverage; behaviour-preserving (existing tests unchanged per AC3) Fixes #1967 Fixes #1947 Co-Authored-By: Claude dev-team-lead <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer <noreply@anthropic.com> Co-Authored-By: Claude product-architect <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
…1950) - Recompute both derived Uk ceilings from the geometry constants rather than re-typing them, so `MAX_SAFE_USAGE_CHUNK_CHARS`'s documented 34-character exceedance fails loudly instead of going stale. The ceiling depends on `USAGE_WIDTH_7COL`, `TABLE_BODY_FONT_SIZE`, `TABLE_SMALL_FONT_SIZE` and `DEFAULT_LINE_HEIGHT`; before this, any of them could move while nothing failed and the only record of the exceedance was a comment a refactor could delete. - Pin only the measured line budgets as literals, labelled as real-render measurements that cannot be derived. Each of the four constants was individually mutated to confirm the guard fires, and the failure message names the constant that moved and points at the rationale comment rather than reporting a bare number. - Account for #1940's continuation-row marker, which adds two rendered characters no budget counts because it is applied post-packing. The base-row overage of 34 chars / 3 lines / 33.6pt becomes 36 / 4 / 44.8 there, and the extra line is bounded by asserting the marker is shorter than one line's capacity. - Pin #1941's editor-renderer coupling — the usage-text input cap must stay below the renderer's 7-column budget — asserted from the rendered `maxlength` attribute, so no production export was needed. Test-only: no production value changes and the rationale comments are untouched. The issue's AC 1.5 was struck on amendment, since the constant it names was removed by #1959 the day after the issue was filed; satisfying it literally would have meant restoring the exact thing this guard exists to keep from silently returning. Fixes #1950 Co-Authored-By: Claude qa-integration-tester <noreply@anthropic.com>
Summary
areaTextandattachmentsNoteare now rendered as a grey inline suffix (\n EG · 1 Anhang) within the usage cell instead of a separate pdfmake stack sub-row; same applies to theReportContentEditorUI (attachments column removed, content moved inline)†(split) and‡(deposit-reduced) footnote markers replaced with inline grey(Teilbetrag)/(abzgl. Abschlag)labels appended to the allocated amount cell, matching the existing deposit note pattern.allocatedMarkers: stringremoved fromReportContentRow; replaced withisSplit: booleanandisDepositReduced: booleanTest plan
npm run lint— no new errors in touched files)npm run format:check)buildReportContent.test.ts,overviewPdf.test.ts,realRender.test.ts,ReportContentEditor.test.tsx,applyAiContent.test.ts,applyOverrides.test.tspass CI(partial)/(less deposit)labels instead of†/‡footnotes🤖 Generated with Claude Code