Story CP-004: Lazy-load payment/receipt bundle + CI bundle-budget gate¶
Module: customer-portal
Slice: CP-004 (architecture §11)
Side: [CUSTOMER] + [BACKEND]
Version target: [V0.0.3]
Priority: 6
Depends on: CP-003 merged
Can develop concurrently with: CP-005
Merge order: After CP-003
Estimated complexity: S
PRD User Stories: N/A - Technical Story (serves CP-NFR-01)
Design reference: N/A — no new screen (bundle/route-split optimisation). Budget basis: docs/design/customer-portal-design.md §7 (connectivity / payload budget)
Wireframe: N/A — bundle/route-split optimisation (no new screen)
[LEGAL-REVIEW] — Inherits the module-wide magic-link
INCERTAINgate. No new legal surface in this slice.
Objective¶
Tighten route-split so the landing critical path stays small and the payment/receipt code loads only when needed, and add a CI bundle-budget gate that fails the build above 180 KB gzipped on the landing path. This is the v0.0.3 optimisation slice — no new business behavior.
Backend Scope¶
Entities¶
None.
Migrations¶
None.
Service Layer¶
None (frontend + CI optimisation). Cache headers per arch §3.4 already in place: static bundle public, max-age=31536000, immutable (hashed filenames).
API Endpoints¶
None.
Validation Rules¶
None.
Multi-Tenant Considerations¶
None (no data path change).
Audit & Logging¶
None.
Frontend Scope¶
Pages & Routes¶
- No new route. Confirm route-split chunks: landing (WelcomePage + ContractList), Summary+Consent, Payment status (CP-08/09), Receipt (CP-07/13) are each lazy and load only on their trigger.
Components¶
- No new components; convert/verify lazy imports (
React.lazy+ Suspense) for the payment + receipt chunks so they load only after the redirect-return.
UI States (ALL REQUIRED)¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | Suspense fallback skeleton while a lazy chunk loads | — |
| Empty | N/A | — |
| Error | Chunk-load failure → retry/reload affordance | « Le chargement a échoué. Réessayer. » |
| Offline / low network | Existing offline banner applies | « Connexion instable. Vos choix sont enregistrés. » |
| Success | Chunk loads on demand; landing path stays light | — |
Responsive Behavior¶
Unchanged from CP-001/002/003 (mobile-first 360 px). No layout change.
Connectivity Behavior (CUSTOMER stories only)¶
- Landing critical path ≤ 200 KB gzipped, with the CI gate enforcing ≤ 180 KB on the landing path specifically.
- Receipt + payment chunks only fetched after the redirect-return — not on the landing path.
- Slow-3G TTI budget on landing ≤ 6 s, FCP ≤ 3 s (arch §8.4) preserved.
Interactions¶
- No new interactions; lazy-chunk loading is transparent with a Suspense skeleton.
Acceptance Criteria¶
AC-004.1: Landing path under the budget
Given a production build
When the CI bundle-budget script measures the landing critical path
Then it is ≤ 180 KB gzipped and the build fails if it exceeds that threshold (CP_BUNDLE_BUDGET_BYTES_GZIPPED)
AC-004.2: Payment/receipt code loads only after redirect-return
Given the customer lands on the contract list
When the landing path loads
Then the payment status and receipt chunks are NOT in the landing bundle; they are fetched only after the PSP redirect-return (or on "Payer" intent for the summary chunk)
Compliance Rules¶
- Inherits module-wide magic-link
INCERTAIN([LEGAL-REVIEW]). No new compliance surface.
Standards & Conventions¶
docs/standards/connectivity-low-bandwidth.md— payload budget, route-split, Slow-3G.docs/standards/react-typescript-guidelines.md— React.lazy + Suspense, code-splitting.docs/standards/tech-stack.md— Vite, vite-bundle-visualizer.docs/standards/git-workflow.md— CI gate wiring.docs/design/customer-portal-design.md— §7 connectivity behaviour (Slow-3G budgets the gate enforces).
Testing Requirements¶
Unit Tests¶
- N/A (build-time gate); a Suspense-fallback render test for the lazy boundaries.
Integration Tests¶
- CI build asserts landing path ≤ 180 KB gzipped (size-budget script).
- E2E: payment/receipt chunk network request only fires after redirect-return.
What QA Will Validate¶
- Slow-3G smoke: landing TTI ≤ 6 s; lazy chunks load on demand without breaking the flow; chunk-load error shows a retry.
Out of Scope¶
- Any new screen or endpoint.
- Link TTL / attempt counter / rate limiting (CP-005).
- SSR/streaming changes beyond existing setup.
Definition of Done¶
- [ ] Route-split verified: landing, summary+consent, payment status, receipt are separate lazy chunks
- [ ] CI bundle-budget gate fails the build above 180 KB gzipped on the landing path
- [ ] Payment/receipt chunks load only after redirect-return (verified in E2E)
- [ ] Suspense fallback + chunk-load error handled (no silent failure)
- [ ] AC-004.1, AC-004.2 pass
- [ ] Slow-3G smoke: landing TTI ≤ 6 s
- [ ] French micro-copy for chunk-load error matches spec