Merged PR 2260: refactor(representations): complete slice-based refactor of representations flow
## Representations Refactor — Behaviour-Preserving Structural Improvements This PR delivers a full refactor of the representations flow, improving structure, readability, and maintainability while preserving all existing behaviour. The work was completed using a controlled, slice-based approach with strict guardrails and regression validation at each step. No changes have been made to user journeys, payloads, routing, or EN/CY behaviour. The result is a cleaner, more maintainable codebase with reduced coupling and clearer separation of concerns, ready for future enhancements without increased risk. --- ## What Was Done The refactor was delivered incrementally across the following slices: - **R1** — Representation entry logic extraction - **R2** — Page loader separation (SSR/data orchestration) - **R3** — Journey step resolution extraction - **R4** — Flow shell decomposition - **R5** — Representation elements normalisation - **R6** — Data/service layer cleanup - **R7** — Summary rendering proof slice - **R8** — Submission/finalisation boundary isolation - **R9** — Summary rollout (Batch 1) Each slice: - was isolated to a single concern - followed strict guardrails - was validated before merge Full detail is available in: `context/representations-refactor-tracker.md` --- ## Key Improvements - Reduced coupling across the representations journey - Separated data loading, orchestration, and rendering concerns - Simplified complex conditional logic into testable helpers - Standardised summary rendering using shared primitives (`SummaryCard`, `SummaryRow`) - Isolated submission/finalisation sequencing into explicit boundaries - Improved overall readability and maintainability --- ## Behaviour Preservation This refactor does **not** change: - User journeys (APP / IP / Agent / LPA) - Route and query behaviour - Payload contracts and API interactions - Redux state shape and usage - Validation rules and messaging - EN/CY behaviour - File upload / PDF / email sequencing - Linked-case logic All changes are structural only. --- ## Validation ### Automated - `npm run lint` — passed (warnings only, no new errors) - `npm run test:reps` — passed (7/7) ### Manual Validated end-to-end across: - APP - IP - Agent - LPA Including: - representation creation - editing/resuming representations - submission flow - confirmation/completion behaviour - summary rendering across case types - EN/CY parity --- ## Risk Management The refactor targeted several high-risk areas: - Case summary entry logic - Representation submission/finalisation sequencing - Dual-mode entry (new vs existing representation) Risk was controlled through: - small, incremental slices - one branch per slice - regression validation per slice - strict behaviour-preservation guardrails - controlled rollout for summary rendering changes --- ## Reviewer Guidance Suggested areas to focus on: - End-to-end representation journey (create → submit → complete) - S...
This commit is contained in:
@@ -258,6 +258,26 @@ export const RenderCondtionalRadioList = ({
|
||||
}) => {
|
||||
const required = (value) => (value ? undefined : "Required");
|
||||
let { t } = useTranslation();
|
||||
|
||||
const yesValue = t("myrepresentations:questionnaire-yes");
|
||||
const noValue = t("myrepresentations:questionnaire-no");
|
||||
|
||||
const normalizeYesNo = (rawValue) => {
|
||||
if (rawValue === true) return "Yes";
|
||||
if (rawValue === false) return "No";
|
||||
if (rawValue === "Yes" || rawValue === yesValue) return "Yes";
|
||||
if (rawValue === "No" || rawValue === noValue) return "No";
|
||||
return rawValue;
|
||||
};
|
||||
|
||||
const isYesSelected = normalizeYesNo(value) === "Yes";
|
||||
|
||||
const requiredIfYes = (fieldValue, allValues) => {
|
||||
const selectedValue = normalizeYesNo(allValues?.[id]);
|
||||
return selectedValue === "Yes" && !String(fieldValue || "").trim()
|
||||
? "Required"
|
||||
: undefined;
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -304,68 +324,84 @@ export const RenderCondtionalRadioList = ({
|
||||
{custom.hint}
|
||||
</div>
|
||||
<div className="govuk-radios">
|
||||
{Object.keys(options).map((key, index) => (
|
||||
<div key={key} className="govuk-!-margin-bottom-3">
|
||||
{Object.keys(options).map((key, index) => {
|
||||
const normalizedOptionValue = normalizeYesNo(
|
||||
options[key]
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="govuk-radios__item"
|
||||
data-children-count={key}
|
||||
key={key}
|
||||
className="govuk-!-margin-bottom-3"
|
||||
>
|
||||
<Field
|
||||
id={id + "_" + index}
|
||||
name={id}
|
||||
component="input"
|
||||
type="radio"
|
||||
className="govuk-radios__input"
|
||||
value={options[key]}
|
||||
validate={[required]}
|
||||
/>
|
||||
<label
|
||||
className="govuk-label govuk-radios__label"
|
||||
htmlFor={id + "_" + index}
|
||||
>
|
||||
{options[key] == "Yes"
|
||||
? t(
|
||||
"myrepresentations:questionnaire-yes"
|
||||
)
|
||||
: t(
|
||||
"myrepresentations:questionnaire-no"
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
{value == "Yes" && options[key] == "Yes" && (
|
||||
<div
|
||||
className="govuk-radios__conditional "
|
||||
id={"conditional-" + id}
|
||||
className="govuk-radios__item"
|
||||
data-children-count={key}
|
||||
key={key}
|
||||
>
|
||||
<div
|
||||
className="govuk-form-group"
|
||||
key={"yes_group_" + key}
|
||||
<Field
|
||||
id={id + "_" + index}
|
||||
name={id}
|
||||
component="input"
|
||||
type="radio"
|
||||
className="govuk-radios__input"
|
||||
value={normalizedOptionValue}
|
||||
validate={[required]}
|
||||
/>
|
||||
<label
|
||||
className="govuk-label govuk-radios__label"
|
||||
htmlFor={id + "_" + index}
|
||||
>
|
||||
<label
|
||||
className="govuk-label"
|
||||
htmlFor={id + "_details"}
|
||||
>
|
||||
{t(
|
||||
"myrepresentations:representation-onbehalfof-label"
|
||||
)}
|
||||
</label>
|
||||
<Field
|
||||
className="govuk-input govuk-!-width-one-half"
|
||||
id={id + "_details"}
|
||||
name={id + "_details"}
|
||||
component={RenderTextfield}
|
||||
type="text"
|
||||
validate={[required]}
|
||||
errorMsg={t(
|
||||
"myrepresentations:is-required-label"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{options[key] == "Yes"
|
||||
? t(
|
||||
"myrepresentations:questionnaire-yes"
|
||||
)
|
||||
: t(
|
||||
"myrepresentations:questionnaire-no"
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{isYesSelected &&
|
||||
normalizedOptionValue === "Yes" && (
|
||||
<div
|
||||
className="govuk-radios__conditional "
|
||||
id={"conditional-" + id}
|
||||
>
|
||||
<div
|
||||
className="govuk-form-group"
|
||||
key={"yes_group_" + key}
|
||||
>
|
||||
<label
|
||||
className="govuk-label"
|
||||
htmlFor={
|
||||
id + "_details"
|
||||
}
|
||||
>
|
||||
{t(
|
||||
"myrepresentations:representation-onbehalfof-label"
|
||||
)}
|
||||
</label>
|
||||
<Field
|
||||
className="govuk-input govuk-!-width-one-half"
|
||||
id={id + "_details"}
|
||||
name={id + "_details"}
|
||||
component={
|
||||
RenderTextfield
|
||||
}
|
||||
type="text"
|
||||
validate={[
|
||||
requiredIfYes
|
||||
]}
|
||||
errorMsg={t(
|
||||
"myrepresentations:is-required-label"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user