feat(web): unify Invoice Detail Page with OrderInvoicePanel redesign#1514
Conversation
Wrap the Document fields block and the invoiceDetailSection provider region in one host card (border/radius/shadow chrome reused from OrderInvoicePanel, #1449), and migrate the bespoke <dl className="invoice-panel__kv"> to the shared KeyValueList primitive. The provider region is now inset by the card's own padding instead of stretching full-width, with a divider + "Provider region" label separating it from the fields above. Layout/framing only — invoiceDetailSection/invoiceCorrectionFlow plugin contract, .invoice-detail__grid column proportions, and the right-column timeline/actions are unchanged. Closes #1462 Signed-off-by: jakubret <jakub.retajczyk@blockydevs.com>
piotrswierzy
left a comment
There was a problem hiding this comment.
Verdict: APPROVE ✅
Clean, tightly-scoped layout refactor that does exactly what #1462 asks and stays consistent with #1449/#1484. No blockers.
What it does
Wraps the Document fields block + the invoiceDetailSection provider region of InvoiceDetailPage in a single host card (reusing OrderInvoicePanel's card chrome — border/radius/shadow), and migrates the bespoke <dl className="invoice-panel__kv"> to the shared KeyValueList primitive via a new buildInvoiceDetailFieldItems helper. The provider region is now inset by the card padding with a divider + "Provider region" label instead of a tone-fill stretching the full width of the main column. Layout/framing only — plugin contract, grid proportions, and the right-column timeline/actions are untouched.
Assessment (docs/frontend-architecture.md + frontend-ui-style-guide.md)
- DRY / #1484 consistency — reuses the shared
KeyValueList+ thebuildInvoiceFieldItems-style helper pattern rather than a parallel renderer; card chrome mirrorsOrderInvoicePanel. ✔ - Plugin seam — host owns card chrome + the capability gate (
InvoiceDetailSection && connection); zero hardcoded KSeF/Subiekt/inFakt logic;invoiceDetailSection/invoiceCorrectionFlowcontract unchanged. ✔ - Dependency direction —
pages → featuresonly; the connections import moved from a deepfeatures/connections/hooks/...path to thefeatures/connectionspublic barrel (which re-exportsConnection+useConnectionsQuery), matchingOrderInvoicePanel. Nice improvement. ✔ - State ownership — server state stays in the existing TanStack Query hooks; no raw
fetch(), no new global state. ✔ - UI / tokens — all colors/radius/shadow/spacing via
var(--…); the rawfont-size: 0.6875remon the caps label matches long-standing precedent acrossindex.css(no font-size token system exists), so not a violation.--tracking-caps,--text-muted,--font-monoused correctly. ✔ - Naming / types — kebab-case file, PascalCase export, explicit
KeyValueItem[]return + typedt; noany. ✔ - Tests — existing
invoice-detail-page.test.tsx(loading / not-found / error / issued / failed+rejected / in-doubt) queries by role/text, so the markup migration keeps assertions green without edits; the linked "Subiekt GT" connection link and "Accepted" clearance still resolve through the newKeyValueList. ✔
BLOCKING
None.
IMPORTANT
None.
SUGGESTION (non-blocking, inline)
buildInvoiceDetailFieldItemsduplicates most of the panel's localbuildInvoiceFieldItems— defensible since the field sets/labels/order differ; revisit only if a third surface appears. (invoice-detail-page.tsx:60)- The panel renders the provider slot with no divider/label while this page adds both — a small cross-surface framing divergence; reads as a conscious, justified choice given the wider main column. (invoice-detail-page.tsx:284)
Optional: a one-line assertion for the new "Provider region" label / card wrapper would lock in the framing, but the existing semantic coverage is sufficient.
Positives
- Faithful reuse of #1449's shared primitives instead of a second visual language.
- Deep-import → public-barrel cleanup for
Connection/useConnectionsQuery. - Provider capability gate and plugin contract left exactly as-is; genuinely framing-only.
| * wrapping markup changed to the shared primitive (#1462, mirrors #1449's | ||
| * `buildInvoiceFieldItems` on `OrderInvoicePanel`). | ||
| */ | ||
| function buildInvoiceDetailFieldItems( |
There was a problem hiding this comment.
SUGGESTION (DRY): buildInvoiceDetailFieldItems mirrors OrderInvoicePanel's local buildInvoiceFieldItems (#1449) closely. The two field sets genuinely differ though — this page adds a linked Invoiced via, a clearanceRef row, and a different field order/labels ("Document type" vs "Document", "Regulatory clearance" vs "Clearance") — so a straight extraction would need parameterizing all of that, and the issue explicitly scoped this as "mirror the pattern," not literal reuse. Fine to keep as-is. If a third invoice-field surface ever appears, that's the moment to lift a shared, parameterized builder into features/invoicing/lib/. No change required.
| {/* Provider extras slot */} | ||
| {InvoiceDetailSection && connection ? ( | ||
| <> | ||
| <hr className="invoice-detail__card-divider" /> |
There was a problem hiding this comment.
SUGGESTION (cross-surface consistency): the redesigned OrderInvoicePanel renders the same invoiceDetailSection slot inline in .order-invoice-panel__body with no divider or label (order-invoice-panel.tsx:368-370), whereas this page adds an <hr className="invoice-detail__card-divider"> + a "Provider region" caps label. #1462 asks for "the same de-emphasised, host-owned framing as #1449," so the two surfaces now diverge slightly. This reads as defensible — the detail page's main column is much wider (3fr 2fr grid) than the panel card, so the label+divider earns its keep in de-emphasising a region that previously stretched full-width. Flagging only so the divergence is a conscious choice; no change required.
Summary
Wrap the Document fields block and the invoiceDetailSection provider region in one host card (border/radius/shadow chrome reused from OrderInvoicePanel, #1449), and migrate the bespoke
to the shared KeyValueList primitive. The provider region is now inset by the card's own padding instead of stretching full-width, with a divider + "Provider region" label separating it from the fields above.-
-
-
- Optional, Docker required:
Layout/framing only — invoiceDetailSection/invoiceCorrectionFlow plugin contract, .invoice-detail__grid column proportions, and the right-column timeline/actions are unchanged.
Related issues
Closes #1462
Quality gate
pnpm lintpasses (zero errors)pnpm type-checkpasses (zero errors)pnpm testpasses (all unit tests green)pnpm test:integrationpasses — neededonly if you touched
apps/api/test/integration/**or any plugin'sinfrastructure/adapters/.By submitting this pull request, I confirm that my contributions
are made under the terms of the Apache License 2.0, and I
certify the Developer Certificate of Origin
by signing off my commits.