## 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...
215 lines
8.1 KiB
JavaScript
215 lines
8.1 KiB
JavaScript
import useTranslation from "next-translate/useTranslation";
|
|
import Head from "next/head";
|
|
import { useState } from "react";
|
|
|
|
import { useSession } from "next-auth/react";
|
|
import { useRouter } from "next/router";
|
|
import { connect } from "react-redux";
|
|
import Breadcrumbs from "../../components/breadcrumbs";
|
|
import CookieBanner from "../../components/cookieBanner";
|
|
import Footer from "../../components/footer";
|
|
import Header from "../../components/header";
|
|
import Case from "../../components/representation";
|
|
import { wrapper } from "../../store/store";
|
|
import { loadRepresentationPage } from "../../lib/representation/pageLoaders";
|
|
|
|
import { setCurrentReference } from "../../store/currentView/action";
|
|
|
|
import NoSessionWarning from "../../components/nosession";
|
|
import ServiceBanner from "../../components/myportal/servicebanner";
|
|
import TimeOut from "../../components/timeout";
|
|
|
|
const RepresentationF = (props) => {
|
|
const { footerLinks, pages } = props;
|
|
let { t, lang } = useTranslation();
|
|
|
|
const router = useRouter();
|
|
const { locale } = router;
|
|
const { appealtypes } = router.query;
|
|
|
|
const [showQuestionnaireSection, setShowQuestionnaireSection] = useState(1);
|
|
|
|
// useEffect(() => {
|
|
// const updateAccountStore = async () => {
|
|
// const thisSession = await getSession();
|
|
|
|
// if (thisSession) {
|
|
// let loggedInUser = await getPortalLoginProxy(
|
|
// thisSession.user.email
|
|
// );
|
|
// dispatch(
|
|
// setAccountDetails(
|
|
// await getPersonalAccount(
|
|
// loggedInUser.value[0].contactid
|
|
// )
|
|
// )
|
|
// );
|
|
// dispatch(setContainerID(thisSession.user.id));
|
|
// dispatch(setLoggedInUserId(loggedInUser.value[0].contactid));
|
|
// //dispatch(setRepresentationSubmit(true));
|
|
// }
|
|
// };
|
|
// updateAccountStore();
|
|
// }, [dispatch]);
|
|
|
|
const { data: session, status } = useSession();
|
|
|
|
// if (status === "loading") {
|
|
// return <NoSessionWarning loading={true} />;
|
|
// }
|
|
|
|
return (
|
|
<div>
|
|
<Head>
|
|
<title>
|
|
{t("case:page-title")} - {t("common:service-name")}
|
|
</title>
|
|
<>
|
|
<link
|
|
rel="canonical"
|
|
href={t("common:gov-wales-link") + router.asPath}
|
|
/>
|
|
<meta
|
|
key="og:locale"
|
|
property="og:locale"
|
|
content={router.locale}
|
|
/>
|
|
{/* If they have a Twitter Handle, include the meta details below */}
|
|
<meta
|
|
key="og:site_name"
|
|
property="og:site_name"
|
|
content={t("common:service-name")}
|
|
/>
|
|
<meta
|
|
key="twitter:site"
|
|
property="twitter:site"
|
|
content="@UKGovWales"
|
|
/>
|
|
|
|
<meta
|
|
name="description"
|
|
content={t("common:service-description")}
|
|
/>
|
|
<meta
|
|
property="og:site_name"
|
|
content={t("common:gov-wales-label")}
|
|
/>
|
|
<meta property="og:title" content={t("case:page-title")} />
|
|
<meta
|
|
property="og:description"
|
|
content={t("common:service-description")}
|
|
/>
|
|
<meta property="og:type" content="website" />
|
|
<meta
|
|
property="og:url"
|
|
content={t("common:gov-wales-link")}
|
|
/>
|
|
<meta
|
|
property="og:image"
|
|
content="https://gov.wales/themes/custom/govwales/images/content/og-global-1200.png"
|
|
/>
|
|
<meta name="twitter:card" content="summary" />
|
|
<meta
|
|
name="twitter:title"
|
|
content={t("common:gov-wales-label")}
|
|
/>
|
|
<meta
|
|
name="twitter:url"
|
|
content={t("common:gov-wales-link") + router.asPath}
|
|
/>
|
|
<meta
|
|
name="twitter:image"
|
|
content="https://gov.wales/themes/custom/govwales/images/content/og-global-120.png"
|
|
/>
|
|
</>
|
|
</Head>
|
|
<div id="page_wrapper">
|
|
<CookieBanner />
|
|
<Header />
|
|
<div className="govuk-width-container">
|
|
<Breadcrumbs
|
|
slug={appealtypes}
|
|
props={props}
|
|
setShowQuestionnaireSection={
|
|
setShowQuestionnaireSection
|
|
}
|
|
showQuestionnaireSection={showQuestionnaireSection}
|
|
/>
|
|
<ServiceBanner props={props} />
|
|
<Case
|
|
myCases={props.myCases.myCases}
|
|
searchResultsObj={
|
|
props.searchResultsObj.searchResultsObj
|
|
}
|
|
searchDetailsObj={
|
|
props.searchResultsObj.searchDetailsObj
|
|
}
|
|
myRepresentations={
|
|
props.myRepresentations.myRepresentations
|
|
}
|
|
watchedCases={props.watchedCases.watchedCases}
|
|
awaitingSubmission={
|
|
props.awaitingSubmission.awaitingSubmission
|
|
}
|
|
caseReference={
|
|
props.currentView.caseReference.currentReference
|
|
}
|
|
ticketnumber={
|
|
props.currentView.caseReference.ticketnumber
|
|
}
|
|
currentType={
|
|
props.currentView.caseReference.currentType
|
|
}
|
|
appealTypeID={
|
|
props.currentView.caseReference.appealType
|
|
}
|
|
incidentid={props.currentView.caseReference.incidentid}
|
|
representationsObj={
|
|
props.searchResultsObj.representationsObj
|
|
}
|
|
props={props}
|
|
setShowQuestionnaireSection={
|
|
setShowQuestionnaireSection
|
|
}
|
|
setCurrentReference={setCurrentReference}
|
|
showQuestionnaireSection={showQuestionnaireSection}
|
|
docsOffline={props.docsOffline}
|
|
/>
|
|
</div>
|
|
<Footer footerLinks={footerLinks} ticketnumber={props} />
|
|
<TimeOut />
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const getServerSideProps = wrapper.getServerSideProps(
|
|
(store) => async (ctx) => loadRepresentationPage({ store, ctx })
|
|
);
|
|
|
|
// const mapDispatchToProps = (dispatch) => {
|
|
// return {
|
|
// setAccountDetails: (loggedInUser) => {
|
|
// dispatch(setAccountDetails(loggedInUser));
|
|
// },
|
|
// };
|
|
// };
|
|
|
|
const mapStateToProps = (state) => {
|
|
return {
|
|
accountDetails: state.accountDetails,
|
|
currentView: state.currentView,
|
|
search: state.search,
|
|
searchResultsObj: state.searchResultsObj,
|
|
formData: state.formData,
|
|
appealType: state.appealType,
|
|
form: state.form,
|
|
myCases: state.myCases,
|
|
watchedCases: state.watchedCases,
|
|
myRepresentations: state.myRepresentations,
|
|
awaitingSubmission: state.awaitingSubmission
|
|
};
|
|
};
|
|
|
|
export default connect(mapStateToProps)(RepresentationF);
|