refactor(representations): preserve Welsh conditional validation and summary parity Related work items: #22441
13 KiB
Representations Refactor Tracker
Base branch: refactor
Status
Current slice: Slice R7 — Summary / Case-Type Rendering Simplification
Status: IMPLEMENTED (manual validation in progress)
Slice List
Slice R1 — Representation Entry Logic Extraction
Goal:
Extract representation eligibility and CTA logic from CaseSummary.
Scope:
- showRepButton logic
- date window checks (showReps / showRepsEnded)
- appeal type branching
- LPA-specific behaviour
- CTA route construction
Rules:
- No behaviour change
- No UI change
- Same inputs/outputs
Completion notes (this slice):
- Extracted representation-entry decision logic from
components/case/summary.jsintocomponents/case/summary/utils/representationEntry.js. - Preserved existing branch structure/order and duplicated consultation/general render branches.
- Preserved existing data-source usage (
detailsObjvssearchDetailsObj) and existing constants/field names. - Preserved local date-check behaviour via extracted local-equivalent helpers (
showRepsLocal,showRepsEndedLocal). - Validation evidence:
npm run lintcompleted (warnings only, no new errors).- Manual regression checks reported as passed (APP/IP/Agent/LPA), including EN/CY parity and route/query behaviour.
Slice R2 — Representation Page Loader Separation
Goal: Separate SSR/data-loading paths.
Scope:
- existing representation (state param)
- new representation (from search/case)
Target:
loadExistingRepresentation()loadNewRepresentation()
Completion notes (this slice):
- Extracted representation page SSR/data-loading orchestration into
lib/representation/pageLoaders.jswith:loadRepresentationBootstrap({ ctx })loadExistingRepresentation({ store, ctx, bootstrap })loadNewRepresentation({ store, ctx, bootstrap })loadRepresentationPage({ store, ctx })
- Kept
pages/myportal/representation.jsrender/UI unchanged and convertedgetServerSidePropsto a thin wrapper callingloadRepresentationPage(...). - Preserved behaviour-critical details:
- shared pre-branch bootstrap fetch timing
- exact branch condition
query.hasOwnProperty("state") - existing dispatch ordering and payload shapes
- existing fallback/data-shape logic and typo-field usage
- unchanged SSR returned props (
containerID,docsOffline)
- Validation evidence:
npm run lintcompleted (warnings only, no new errors).npm run test:repsre-run completed: 6 passed, 1 failed.- Failing test:
[chromium] tests/loggedin/raiserep.spec.js:203:5(Raise Statement representation as an Interested Party) with errorno statement option/ missingStatementoption in combobox. - Manual regression checks confirmed passed (APP/IP/Agent/LPA), including EN/CY parity.
Slice R3 — Journey Step Resolution Extraction
Goal: Isolate step navigation logic.
Scope:
- current step determination
- next/previous step logic
- validation gating between steps
Completion notes (this slice):
- Added pure step-resolution helpers in
components/case/representation/utils/stepResolution.js:resolveJourneyStageFlags({ representationSubmit, representationSubmitConfirmation })resolveRepresentationControlKey({ isLPA, representationCapacity, repDetailsCapacity, appealType, normalizeCapacity })resolveSubmitTransition({ representationType, showQuestionnaireSection, questionnaireCount })getQuestionnaireNextSection(currentSection, questionnaireCount)getQuestionnairePreviousSection(currentSection)
- Updated
components/case/representation/index.jsto call helpers for:- check-stage flag resolution (
showCheckStage) - control key resolution in
whichControl() - questionnaire/non-questionnaire submit transition decision
- questionnaire next-section increment calculation
- check-stage flag resolution (
- Preserved behaviour-critical constraints:
- no JSX structure redesign
- no route/query changes
- no payload/Redux shape changes
- no dispatch/API side-effect sequencing changes
- no representation-type availability logic changes (
buildRepsArrunchanged)
- Validation evidence:
npm run lintcompleted (warnings only, no new errors).npm run test:repsre-run completed: 6 passed.- Manual APP/IP/Agent/LPA and EN/CY checks: pending for this slice.
Slice R4 — Flow Shell Decomposition
Goal: Break large flow component into smaller parts.
Target components:
- Capacity selection
- Representation type selection
- Content entry
- Check answers
- Completion
Slice R5 — Representation Elements Normalisation
Goal: Clean up shared UI helpers and duplicated patterns.
Scope:
- input rendering
- file upload handling (structure only, not behaviour)
- shared UI logic extraction
Completion notes (this slice):
- Implemented on feature branch created from
refactor:rep-slice-r5-elements-normalisation. - Added render-only shared fragments under
components/case/representation/elements/:RepresentationTypeSelectorBlock.jsRepresentationActionButtons.js
- Integrated these fragments into main representation variants only:
representationAppellant.jsrepresentationAgent.jsrepresentationInterestedPartyPerson.js
- Preserved scope constraints for Slice R5:
- kept parent components as logic owners
- no submission/update side-effect refactor
- no upload service behaviour changes
- no payload/route/query/Redux/validation business-rule changes
- consultation variants intentionally not normalised in this slice
- Validation evidence:
npm run lintcompleted (warnings only, no new errors).npm run test:repscompleted: 7 passed.- Manual APP/IP/Agent/LPA checks: passed.
- Manual EN/CY parity checks: passed.
Slice R6 — Async Data / Service Layer Cleanup
Goal: Reduce duplication and coupling in data fetching.
Scope:
- getCase → getPortalModuleDetails chains
- blob/file retrieval
- representation detail aggregation
Completion notes (this slice):
- Implemented on feature branch created from
refactor:rep-slice-r6-data-service-cleanup. - Added deterministic representation-specific data-resolution helpers in:
components/case/representation/utils/dataResolution.js
- Updated
components/case/representation/index.jsto use extracted helper functions for:- initial case details/results source resolution
- details/results lookup by current context (state/edit/ticket/created)
- submit-path details source resolution and details lookup
- Preserved slice boundary constraints:
- no service/API call execution movement
- no dispatch sequencing changes
- no loader branch redesign
- no blob retrieval orchestration changes
- no payload contract/Redux/route/query/EN-CY changes
- Validation evidence:
npm run lintcompleted (warnings only, no new errors).npm run test:repscompleted: 7 passed.- Manual APP/IP/Agent/LPA checks: passed.
- Manual EN/CY parity checks: passed.
Slice R7 — Summary / Case-Type Rendering Simplification (Optional)
Goal: Reduce duplication in summary-type components.
Scope:
- unify repeated rendering patterns
- introduce shared summary renderer where safe
Completion notes (this slice):
- Implemented on feature branch created from
refactor:rep-slice-r7-summary-rendering-simplification. - Added render-only summary primitives:
components/case/summary/components/SummaryCard.jscomponents/case/summary/components/SummaryRow.js
- Applied these primitives to a small proof subset of summary-type files only:
components/case/summaryTypes/pinswg_planningappeals78id.jscomponents/case/summaryTypes/pinswg_planningconditionss73s79id.js
- Preserved Slice R7 constraints:
- no CaseSummary orchestration changes
- no translation/date/bilingual logic moved into shared primitives
- no linked-cases extraction (kept inline)
- no row/card order changes intended
- no route/query/payload/Redux/business-rule changes
- Validation evidence:
npm run lintcompleted (warnings only, no new errors).npm run test:repscompleted: 7 passed.- Manual APP/IP/Agent/LPA and EN/CY checks: in progress (Welsh IP statement option-label parity issue detected and patched in representation flow check display).
Post-R7 follow-up patch — Welsh IP statement label parity
Issue observed during manual check:
- In Welsh journey (Interested Party → Statement), option-label parity regressed:
- selection step showed duplicated Welsh “No” labels
- check/submit summary displayed raw English
No
Fix applied (minimal, behaviour-preserving):
components/case/representation/representationInterestedPartyPerson.js- restored canonical option values for conditional radio to
options={["Yes", "No"]}soRenderCondtionalRadioListtranslation mapping behaves correctly.
- restored canonical option values for conditional radio to
components/case/representation/representationCompleteSubmit.js- added display mapping for
representationOnBehalfOfsummary value:Yes→t("myrepresentations:questionnaire-yes")No→t("myrepresentations:questionnaire-no")- fallback unchanged for non-boolean/unexpected values
- added display mapping for
Validation evidence (follow-up patch):
npm run lintcompleted (warnings only, no new errors).npm run test:repscompleted: 7 passed (4.6m).- Manual Welsh IP statement check: input labels and check-summary label now aligned via translation mapping.
Post-R7 follow-up patch 2 — Welsh IP conditional details submit validation
Issue observed during manual check:
- In Welsh journey (Interested Party → Statement), when selecting “Yes” and entering conditional details, submit could still block with required validation on the conditional text field.
Fix applied (minimal, behaviour-preserving):
components/case/representation/representationElements.js- In
RenderCondtionalRadioList, made “Yes selected” detection bilingual-safe and value-safe:- accepts canonical value (
"Yes"), translated display value (t("myrepresentations:questionnaire-yes")), and booleantrue.
- accepts canonical value (
- Replaced unconditional conditional-field validator with context-aware validator:
requiredIfYesonly enforces required when “Yes” is selected.
- Updated conditional rendering check to use the same normalized
isYesSelectedguard.
- In
Validation evidence (follow-up patch 2):
npm run lintcompleted (warnings only, no new errors).npm run test:repsrun completed with 6 passed / 1 failed.- Failure:
[chromium] Raise representation as an Appellanttimed out waiting fordd a.govuk-link--no-underline(entry-link locator timeout), not in Welsh IP conditional details path.
- Failure:
Post-R7 follow-up patch 3 — Welsh conditional required check tied to current radio value
Issue observed during manual check:
- In Welsh journey, selecting “Yes” could still leave the conditional details field blocked with required validation (
Yn ofynnol) after entering text, preventing progress.
Fix applied (minimal, behaviour-preserving):
components/case/representation/representationElements.js- Added explicit
normalizeYesNo(...)mapping for both selected value and option values:- canonical/translated/boolean values normalize to
"Yes"/"No".
- canonical/translated/boolean values normalize to
- Radio
Fieldvalues now use normalized canonical values (preserves existing payload expectation). - Updated conditional
requiredIfYesvalidator to evaluate against the current form value (allValues[id]) rather than closure-only value. - Required check now trims whitespace and only errors when selected value resolves to
"Yes"and details are empty.
- Added explicit
Validation evidence (follow-up patch 3):
npm run lintcompleted (warnings only, no new errors).npm run test:repsintentionally not run per user instruction while manual Welsh verification is in progress.
Slice R8 — Submission / Finalisation Boundary Isolation
Goal: Isolate submission logic.
Scope:
- payload shaping
- submit calls
- completion handling
Risk Level: HIGH
(Must only be done after earlier slices stabilised)
Rules
- Only work on ONE slice at a time
- Do not combine slices
- One branch per slice
- One commit per slice
- Update tracker after completion
- Do not proceed if regression risk exists
Regression Checklist (Run After Each Slice)
Core Journey
- Navigate from case summary → Make representation
- Select capacity
- Select representation type
- Enter content
- Upload files (if applicable)
- View check answers
- Submit representation
- View confirmation page
Validation
- EN/CY parity maintained
- No navigation regressions
- No state inconsistencies
- No console errors
Technical
npm run lintpasses- No new warnings/errors introduced
Notes
- CaseSummary is a high-risk entry point
- Eligibility logic is complex and must not drift
- Submission flow is highly sensitive
- Dual-mode entry (new vs existing representation) is a key complexity
Definition of Done (Per Slice)
A slice is complete when:
- behaviour is preserved
- regression checks pass
- code is cleaner and easier to understand
- change is small and safe to merge