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:
+155
-197
@@ -37,14 +37,21 @@ import * as CaseTypes from "./summaryTypes"; // ✅ must match filename exactly
|
||||
import {
|
||||
capitalizeFirstLetter,
|
||||
formatDates,
|
||||
showReps,
|
||||
hasValidKey,
|
||||
isObjectNotEmpty,
|
||||
hasOwnPropertyAndNotNull,
|
||||
linkedCasesList,
|
||||
showRepsEnded,
|
||||
getBilingualText
|
||||
} from "./summary/utils/helpers";
|
||||
import {
|
||||
isConsultationWindowOpen,
|
||||
isGeneralRepresentationWindowOpen,
|
||||
canShowRepButtonForAppealType,
|
||||
getEntryCtaLabelKey,
|
||||
shouldSuppressBlockedMessage,
|
||||
getGeneralBlockedEndDate,
|
||||
isRepresentationPeriodEnded
|
||||
} from "./summary/utils/representationEntry";
|
||||
import {
|
||||
resolveSearchResultsHref,
|
||||
isDnsRoutePath
|
||||
@@ -806,16 +813,22 @@ const CaseSummary = (props) => {
|
||||
appealTypeID
|
||||
).PrimaryIdAttribute ==
|
||||
"pinswg_sipscase" &&
|
||||
showReps(
|
||||
detailsObj.pinswg_consultationopen,
|
||||
detailsObj.pinswg_consultationclose
|
||||
isConsultationWindowOpen(
|
||||
detailsObj
|
||||
) && (
|
||||
<>
|
||||
{showRepButton(
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType
|
||||
{canShowRepButtonForAppealType(
|
||||
{
|
||||
"appealType":
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType,
|
||||
"isLPA":
|
||||
isLPA,
|
||||
"searchDetailsObj":
|
||||
props.searchDetailsObj
|
||||
}
|
||||
) ? (
|
||||
<>
|
||||
<Link
|
||||
@@ -838,25 +851,27 @@ const CaseSummary = (props) => {
|
||||
}}
|
||||
className="govuk-button"
|
||||
>
|
||||
{props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType ==
|
||||
846040002
|
||||
? t(
|
||||
"case:summary-make-consultation-label"
|
||||
)
|
||||
: t(
|
||||
"case:summary-make-representation-label"
|
||||
)}
|
||||
{t(
|
||||
getEntryCtaLabelKey(
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType
|
||||
)
|
||||
)}
|
||||
</Link>
|
||||
</>
|
||||
) : !isLPA &&
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType ==
|
||||
846040004 ? (
|
||||
) : shouldSuppressBlockedMessage(
|
||||
{
|
||||
"appealType":
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType,
|
||||
"isLPA":
|
||||
isLPA
|
||||
}
|
||||
) ? (
|
||||
""
|
||||
) : (
|
||||
<>
|
||||
@@ -899,114 +914,122 @@ const CaseSummary = (props) => {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(_.has(
|
||||
detailsObj,
|
||||
"pinswg_startdate"
|
||||
) ||
|
||||
_.has(
|
||||
detailsObj,
|
||||
"pinswg_applicationacceptedasvalid"
|
||||
) ||
|
||||
_.has(
|
||||
detailsObj,
|
||||
"pinswg_startdates"
|
||||
)) &&
|
||||
showReps(
|
||||
detailsObj.pinswg_startdate ||
|
||||
detailsObj.pinswg_startdates ||
|
||||
detailsObj.pinswg_applicationacceptedasvalid,
|
||||
detailsObj.pinswg_finalcommentsduedate ||
|
||||
detailsObj.pinswg_endofrepresentationperiod
|
||||
) && (
|
||||
<>
|
||||
{showRepButton(
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType
|
||||
) ? (
|
||||
<>
|
||||
<Link
|
||||
href={{
|
||||
pathname:
|
||||
"/myportal/representation",
|
||||
query: {
|
||||
case:
|
||||
currentType ==
|
||||
"searchResultsObj"
|
||||
{isGeneralRepresentationWindowOpen(
|
||||
detailsObj
|
||||
) && (
|
||||
<>
|
||||
{canShowRepButtonForAppealType(
|
||||
{
|
||||
"appealType":
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType,
|
||||
"isLPA":
|
||||
isLPA,
|
||||
"searchDetailsObj":
|
||||
props.searchDetailsObj
|
||||
}
|
||||
) ? (
|
||||
<>
|
||||
<Link
|
||||
href={{
|
||||
pathname:
|
||||
"/myportal/representation",
|
||||
query: {
|
||||
case:
|
||||
currentType ==
|
||||
"searchResultsObj"
|
||||
? detailsObj.ticketnumber
|
||||
: currentType ==
|
||||
"watchedCases"
|
||||
? casesObj.pinswg_title
|
||||
: currentType ==
|
||||
"directResultsObj"
|
||||
? detailsObj.ticketnumber
|
||||
: currentType ==
|
||||
"watchedCases"
|
||||
? casesObj.pinswg_title
|
||||
: currentType ==
|
||||
"directResultsObj"
|
||||
? detailsObj.ticketnumber
|
||||
: casesObj.reference
|
||||
}
|
||||
}}
|
||||
className="govuk-button"
|
||||
>
|
||||
{t(
|
||||
"case:summary-make-representation-label"
|
||||
)}
|
||||
</Link>
|
||||
</>
|
||||
) : !isLPA &&
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType ==
|
||||
846040004 ? (
|
||||
""
|
||||
) : (
|
||||
<>
|
||||
<div className="govuk-grid-column-full govuk-!-padding-left-0">
|
||||
<div className="govuk-button-group">
|
||||
<p className="govuk-body">
|
||||
{t(
|
||||
"case:representation-date-passed-label",
|
||||
{
|
||||
startDate:
|
||||
formatDates(
|
||||
detailsObj.pinswg_startdate
|
||||
),
|
||||
endDate:
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType ==
|
||||
846040019
|
||||
? formatDates(
|
||||
detailsObj.pinswg_statementduedate
|
||||
: casesObj.reference
|
||||
}
|
||||
}}
|
||||
className="govuk-button"
|
||||
>
|
||||
{t(
|
||||
"case:summary-make-representation-label"
|
||||
)}
|
||||
</Link>
|
||||
</>
|
||||
) : shouldSuppressBlockedMessage(
|
||||
{
|
||||
"appealType":
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType,
|
||||
"isLPA":
|
||||
isLPA
|
||||
}
|
||||
) ? (
|
||||
""
|
||||
) : (
|
||||
<>
|
||||
<div className="govuk-grid-column-full govuk-!-padding-left-0">
|
||||
<div className="govuk-button-group">
|
||||
<p className="govuk-body">
|
||||
{t(
|
||||
"case:representation-date-passed-label",
|
||||
{
|
||||
startDate:
|
||||
formatDates(
|
||||
detailsObj.pinswg_startdate
|
||||
),
|
||||
endDate:
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType ==
|
||||
846040019
|
||||
? formatDates(
|
||||
getGeneralBlockedEndDate(
|
||||
detailsObj,
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType
|
||||
)
|
||||
: formatDates(
|
||||
detailsObj.pinswg_finalcommentsduedate
|
||||
)
|
||||
: formatDates(
|
||||
getGeneralBlockedEndDate(
|
||||
detailsObj,
|
||||
props
|
||||
.currentView
|
||||
.caseReference
|
||||
.appealType
|
||||
)
|
||||
}
|
||||
)}
|
||||
<br />{" "}
|
||||
{t(
|
||||
"case:representation-date-passed-additional-label"
|
||||
)}{" "}
|
||||
<Link
|
||||
href={
|
||||
"mailto:" +
|
||||
t(
|
||||
"home:login-contact-email"
|
||||
)
|
||||
}
|
||||
>
|
||||
{t(
|
||||
)
|
||||
}
|
||||
)}
|
||||
<br />{" "}
|
||||
{t(
|
||||
"case:representation-date-passed-additional-label"
|
||||
)}{" "}
|
||||
<Link
|
||||
href={
|
||||
"mailto:" +
|
||||
t(
|
||||
"home:login-contact-email"
|
||||
)}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
>
|
||||
{t(
|
||||
"home:login-contact-email"
|
||||
)}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Link
|
||||
className="govuk-button govuk-button--secondary"
|
||||
@@ -1352,9 +1375,8 @@ const CaseSummary = (props) => {
|
||||
null ||
|
||||
detailsObj.pinswg_finalcommentsduedate !=
|
||||
null) &&
|
||||
showRepsEnded(
|
||||
detailsObj.pinswg_startdate,
|
||||
detailsObj.pinswg_finalcommentsduedate
|
||||
isRepresentationPeriodEnded(
|
||||
detailsObj
|
||||
) && (
|
||||
<div className="govuk-body">
|
||||
{props.currentView
|
||||
@@ -2043,70 +2065,6 @@ const CaseSummary = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
function showReps(startDate, endDate) {
|
||||
let date = new Date();
|
||||
date = new Date(date.toDateString());
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
return date >= start && date <= end ? true : false;
|
||||
}
|
||||
|
||||
function showRepButton(appealType) {
|
||||
switch (appealType) {
|
||||
case 846040012:
|
||||
case 846040013:
|
||||
case 846040014:
|
||||
//for ROW case 846040015:
|
||||
case 846040020:
|
||||
case 846040021:
|
||||
case 846040023:
|
||||
case 846040024:
|
||||
return false;
|
||||
case 846040004:
|
||||
return isLPA ? true : false;
|
||||
|
||||
case 846040015:
|
||||
return props.searchDetailsObj[0].value[0]
|
||||
.pinswg_specialistcaseprocess == 846040001
|
||||
? showReps(
|
||||
props.searchDetailsObj[0].value[0].pinswg_startdate,
|
||||
props.searchDetailsObj[0].value[0]
|
||||
.pinswg_finalcommentsduedate
|
||||
)
|
||||
: true;
|
||||
|
||||
case 846040018:
|
||||
let shouldShow =
|
||||
props.searchDetailsObj[0].value[0]
|
||||
.pinswg_speacialistcaseprocess == 846040000 && isLPA
|
||||
? true
|
||||
: props.searchDetailsObj[0].value[0]
|
||||
.pinswg_speacialistcaseprocess == 846040001
|
||||
? true
|
||||
: false;
|
||||
|
||||
return shouldShow;
|
||||
|
||||
case 846040019:
|
||||
return showReps(
|
||||
props.searchDetailsObj[0].value[0].pinswg_startdate,
|
||||
props.searchDetailsObj[0].value[0].pinswg_statementduedate
|
||||
);
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function showRepsEnded(startDate, endDate) {
|
||||
let date = new Date();
|
||||
date = new Date(date.toDateString());
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
return date > start && date > end ? true : false;
|
||||
}
|
||||
|
||||
if (status == "authenticated" && cookies.pedwWatchCase != null) {
|
||||
selectWatchedCase(
|
||||
props.accountDetails.accountDetails.contactid,
|
||||
|
||||
Reference in New Issue
Block a user