Merged PR 2251: refactor(representations): separate representation page SSR loaders into lib/...

refactor(representations): separate representation page SSR loaders into lib/representation/pageLoaders (Slice R2, behaviour-preserving)

Related work items: #22441
This commit is contained in:
Robert Bond
2026-04-17 05:30:38 +00:00
parent 011be977a2
commit a43eb76e3a
3 changed files with 297 additions and 302 deletions
+21 -1
View File
@@ -6,7 +6,7 @@ Base branch: `refactor`
## Status
Current slice: Slice R1 — Representation Entry Logic Extraction
Current slice: Slice R2 — Representation Page Loader Separation
Status: COMPLETE
---
@@ -61,6 +61,26 @@ Separate SSR/data-loading paths.
- `loadExistingRepresentation()`
- `loadNewRepresentation()`
**Completion notes (this slice):**
- Extracted representation page SSR/data-loading orchestration into `lib/representation/pageLoaders.js` with:
- `loadRepresentationBootstrap({ ctx })`
- `loadExistingRepresentation({ store, ctx, bootstrap })`
- `loadNewRepresentation({ store, ctx, bootstrap })`
- `loadRepresentationPage({ store, ctx })`
- Kept `pages/myportal/representation.js` render/UI unchanged and converted `getServerSideProps` to a thin wrapper calling `loadRepresentationPage(...)`.
- 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 lint` completed (warnings only, no new errors).
- `npm run test:reps` re-run completed: **6 passed, 1 failed**.
- Failing test: `[chromium] tests/loggedin/raiserep.spec.js:203:5` (`Raise Statement representation as an Interested Party`) with error `no statement option` / missing `Statement` option in combobox.
- Manual regression checks confirmed passed (APP/IP/Agent/LPA), including EN/CY parity.
---
### Slice R3 — Journey Step Resolution Extraction
+270
View File
@@ -0,0 +1,270 @@
import { getSession } from "next-auth/react";
import {
getPersonalAccount,
getPortalLogin
} from "../../actions/services/accountService";
import {
getCase,
getPortalModuleDetails
} from "../../actions/services/caseService";
import { getRepsFromBlob } from "../../actions/services/documentService";
import { getBasicSearch } from "../../actions/services/searchService";
import {
getFormCollectionByID,
getSearchDetails
} from "../../components/utils";
import {
setAccountDetails,
setContainerID
} from "../../store/accountDetails/action";
import {
setCurrentReference,
setCurrentView,
setFilesForRepresentations,
setRepresentationCapacity
} from "../../store/currentView/action";
import {
setMyRepresentations,
setMyRepresentationsDetails
} from "../../store/myRepresentations/action";
import {
setSearchDetails,
setSearchResults
} from "../../store/searchOutput/action";
import { setSearch } from "../../store/search/action";
import { getRepsFilesBlobs } from "../../actions/azurestorage";
const getDetails = (resultsObj, detailsType) => {
let detailsArr = [];
resultsObj =
detailsType == "mySubmittedReps" ? resultsObj : resultsObj.value;
if (detailsType == "myRepresentations") {
const repsObj = resultsObj.map((searchDetail, index) => {
detailsArr.push(
getCase(searchDetail["incidentID"]).then((data) => {
let caseID = "";
switch (detailsType) {
case "myCases":
caseID = data.pinswg_title;
break;
case "myWatchedCases":
case "mySubmittedReps":
caseID =
data[
"_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
];
break;
case "awaitingSubmission":
case "myRepresentations":
caseID = data.ticketnumber;
break;
default:
caseID = data.pinswg_title;
}
return getPortalModuleDetails(
getFormCollectionByID(data.pinswg_appealcasetype)
.LogicalCollectionName,
caseID
);
})
);
});
}
let detArr = Promise.all(detailsArr);
console.log(detArr);
return detArr;
};
export const loadRepresentationBootstrap = async ({ ctx }) => {
const { query } = ctx;
console.log("The query", query);
let thisSession = await getSession(ctx);
let loggedInUser = {};
if (!thisSession) {
console.log("not has sesssion.......");
return {
redirect: {
destination: "/auth/signin",
permanent: false
}
};
} else {
[loggedInUser] = await Promise.all([
await getPortalLogin(thisSession.user.email)
]);
console.log(
"=====//////////=====",
thisSession,
thisSession.user.email,
loggedInUser
);
loggedInUser = loggedInUser.value[0].contactid;
}
const [accountDetails, myRepresentations] = await Promise.all([
await getPersonalAccount(loggedInUser),
await getRepsFromBlob(thisSession.user.id)
]);
const myRepresentationsDetails = await getDetails(
myRepresentations,
"myRepresentations"
);
console.log(accountDetails);
const isLPA =
accountDetails[
"pinswg_typeofinvolvement@OData.Community.Display.V1.FormattedValue"
] == "LPA"
? true
: false;
return {
query,
thisSession,
loggedInUser,
accountDetails,
myRepresentations,
myRepresentationsDetails,
isLPA
};
};
export const loadExistingRepresentation = async ({ store, ctx, bootstrap }) => {
const { query } = ctx;
const {
thisSession,
myRepresentations,
myRepresentationsDetails,
accountDetails,
isLPA
} = bootstrap;
console.log("is lpa?:", isLPA);
store.dispatch(setContainerID(thisSession.user.id));
store.dispatch(setMyRepresentations(myRepresentations));
store.dispatch(setMyRepresentationsDetails(myRepresentationsDetails));
function findObjectByKeyValue(arr, key, value) {
return arr.find((obj) => obj[key] === value);
}
const result = findObjectByKeyValue(
myRepresentations.value,
"repfile_name",
query.created
);
store.dispatch(
setCurrentReference({
"ticketnumber":
result.ticketnumber || result.caseRef || result.pinswg_name,
"currentReference": result.caseRef,
"currentType": "myRepresentations",
"incidentid": result.incidentID,
"appealType": result.appealType,
"repDetails": result,
"filesList": result.filesList
})
);
store.dispatch(setRepresentationCapacity(result.representationCapacity));
const repsFileListObj = await getRepsFilesBlobs(
result.containerID,
result.ticketnumber || result.caseRef,
result.repfile_name
);
store.dispatch(setAccountDetails(accountDetails));
store.dispatch(setFilesForRepresentations(repsFileListObj));
return {
props: {
containerID: thisSession.user.id,
docsOffline: process.env.DOCAPI_OFFLINE || false
}
};
};
export const loadNewRepresentation = async ({ store, ctx, bootstrap }) => {
const { query } = ctx;
const { thisSession, accountDetails } = bootstrap;
const searchResultsObj = await getBasicSearch(query.case);
const searchDetailsObj = await getSearchDetails(searchResultsObj);
store.dispatch(setContainerID(thisSession.user.id));
store.dispatch(setSearchResults(searchResultsObj));
store.dispatch(setSearchDetails(searchDetailsObj));
store.dispatch(setSearch(query.case));
store.dispatch(
setCurrentView({
"viewName": "My Representations",
"viewKey": "myRepresentations"
})
);
console.log(
accountDetails,
"===================== is nrw",
accountDetails.emailaddress1.includes(process.env.NRWDOMAIN),
"===================== "
);
store.dispatch(
setCurrentReference({
"isNRW":
accountDetails.emailaddress1.includes(process.env.NRWDOMAIN) ||
false,
"ticketnumber": searchResultsObj.value[0].ticketnumber,
"currentReference": searchResultsObj.value[0].title,
"currentType": "myRepresentations",
"incidentid": searchResultsObj.value[0].incidentid,
"appealType": searchResultsObj.value[0].pinswg_appealcasetype,
"specialistProcess":
searchDetailsObj[0].value[0].pinswg_speacialistcaseprocess !=
null ||
searchDetailsObj[0].value[0].pinswg_specialistcaseprocess !=
null
? searchDetailsObj[0].value[0]
.pinswg_speacialistcaseprocess ||
searchDetailsObj[0].value[0].pinswg_specialistcaseprocess
: ""
})
);
store.dispatch(setAccountDetails(accountDetails));
return {
props: {
containerID: thisSession.user.id,
docsOffline: process.env.DOCAPI_OFFLINE || false
}
};
};
export const loadRepresentationPage = async ({ store, ctx }) => {
const bootstrap = await loadRepresentationBootstrap({ ctx });
if (bootstrap?.redirect) {
return bootstrap;
}
const { query } = ctx;
if (query.hasOwnProperty("state")) {
return loadExistingRepresentation({ store, ctx, bootstrap });
}
return loadNewRepresentation({ store, ctx, bootstrap });
};
+6 -301
View File
@@ -1,62 +1,24 @@
import useTranslation from "next-translate/useTranslation";
import Head from "next/head";
import { useEffect, useState } from "react";
import { useState } from "react";
import { getSession, useSession } from "next-auth/react";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { connect, useDispatch } from "react-redux";
import {
getPersonalAccount,
getPortalLogin,
getPortalLoginProxy
} from "../../actions/services/accountService";
import {
getCase,
getPortalModuleDetails
} from "../../actions/services/caseService";
import {
getFilesFromBlob,
getRepsFromBlob
} from "../../actions/services/documentService";
import { getBasicSearch } from "../../actions/services/searchService";
import { getFormCollectionByID } from "../../components/utils";
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 {
setAccountDetails,
setContainerID,
setLoggedInUserId
} from "../../store/accountDetails/action";
import { wrapper } from "../../store/store";
import { getSearchDetails } from "../../components/utils";
import { loadRepresentationPage } from "../../lib/representation/pageLoaders";
import {
setCurrentReference,
setCurrentView,
setFilesForRepresentations,
setRepresentationCapacity
} from "../../store/currentView/action";
import {
setSearchDetails,
setSearchResults
} from "../../store/searchOutput/action";
import { setSearch } from "../../store/search/action";
import { setCurrentReference } from "../../store/currentView/action";
import NoSessionWarning from "../../components/nosession";
import ServiceBanner from "../../components/myportal/servicebanner";
import TimeOut from "../../components/timeout";
import {
setMyRepresentations,
setMyRepresentationsDetails,
setMySubmittedReps,
setMySubmittedRepsDetails
} from "../../store/myRepresentations/action";
import { getRepsFilesBlobs } from "../../actions/azurestorage";
const RepresentationF = (props) => {
const { footerLinks, pages } = props;
let { t, lang } = useTranslation();
@@ -65,8 +27,6 @@ const RepresentationF = (props) => {
const { locale } = router;
const { appealtypes } = router.query;
const dispatch = useDispatch();
const [showQuestionnaireSection, setShowQuestionnaireSection] = useState(1);
// useEffect(() => {
@@ -224,264 +184,9 @@ const RepresentationF = (props) => {
};
export const getServerSideProps = wrapper.getServerSideProps(
(store) => async (ctx) => {
const { query, req, res } = ctx;
console.log("The query", query);
const { cookies } = req;
let loggedInUserCookie = cookies.pinsUser;
let thisSession = await getSession(ctx);
let loggedInUser = {};
if (!thisSession) {
console.log("not has sesssion.......");
return {
redirect: {
destination: "/auth/signin",
permanent: false
}
};
} else {
[loggedInUser] = await Promise.all([
await getPortalLogin(thisSession.user.email)
]);
console.log(
"=====//////////=====",
thisSession,
thisSession.user.email,
loggedInUser
);
loggedInUser = loggedInUser.value[0].contactid;
}
const [accountDetails, myRepresentations] = await Promise.all([
await getPersonalAccount(loggedInUser),
await getRepsFromBlob(thisSession.user.id)
]);
const myRepresentationsDetails = await getDetails(
myRepresentations,
"myRepresentations"
);
console.log(accountDetails);
const isLPA =
accountDetails[
"pinswg_typeofinvolvement@OData.Community.Display.V1.FormattedValue"
] == "LPA"
? true
: false;
//isLPA && store.dispatch(setRepresentationCapacity("LPA"));
if (query.hasOwnProperty("state")) {
console.log("is lpa?:", isLPA);
store.dispatch(setContainerID(thisSession.user.id));
store.dispatch(setMyRepresentations(myRepresentations));
store.dispatch(
setMyRepresentationsDetails(myRepresentationsDetails)
);
function findObjectByKeyValue(arr, key, value) {
return arr.find((obj) => obj[key] === value);
}
const result = findObjectByKeyValue(
myRepresentations.value,
"repfile_name",
query.created
);
store.dispatch(
setCurrentReference({
"ticketnumber":
result.ticketnumber ||
result.caseRef ||
result.pinswg_name,
"currentReference": result.caseRef,
"currentType": "myRepresentations",
"incidentid": result.incidentID,
"appealType": result.appealType,
"repDetails": result,
"filesList": result.filesList
})
);
store.dispatch(
setRepresentationCapacity(result.representationCapacity)
);
// console.log(
// "===========================\n getting files",
// result.filesList
// );
const repsFileListObj = await getRepsFilesBlobs(
result.containerID,
result.ticketnumber || result.caseRef,
result.repfile_name
);
store.dispatch(setAccountDetails(accountDetails));
store.dispatch(setFilesForRepresentations(repsFileListObj));
return {
props: {
containerID: thisSession.user.id,
docsOffline: process.env.DOCAPI_OFFLINE || false
}
};
} else {
const searchResultsObj = await getBasicSearch(query.case);
const searchDetailsObj = await getSearchDetails(searchResultsObj);
store.dispatch(setContainerID(thisSession.user.id));
store.dispatch(setSearchResults(searchResultsObj));
store.dispatch(setSearchDetails(searchDetailsObj));
store.dispatch(setSearch(query.case));
store.dispatch(
setCurrentView({
"viewName": "My Representations",
"viewKey": "myRepresentations"
})
);
console.log(
accountDetails,
"===================== is nrw",
accountDetails.emailaddress1.includes(process.env.NRWDOMAIN),
"===================== "
);
store.dispatch(
setCurrentReference({
"isNRW":
accountDetails.emailaddress1.includes(
process.env.NRWDOMAIN
) || false,
"ticketnumber": searchResultsObj.value[0].ticketnumber,
"currentReference": searchResultsObj.value[0].title,
"currentType": "myRepresentations",
"incidentid": searchResultsObj.value[0].incidentid,
"appealType":
searchResultsObj.value[0].pinswg_appealcasetype,
"specialistProcess":
searchDetailsObj[0].value[0]
.pinswg_speacialistcaseprocess != null ||
searchDetailsObj[0].value[0]
.pinswg_specialistcaseprocess != null
? searchDetailsObj[0].value[0]
.pinswg_speacialistcaseprocess ||
searchDetailsObj[0].value[0]
.pinswg_specialistcaseprocess
: ""
})
);
store.dispatch(setAccountDetails(accountDetails));
return {
props: {
containerID: thisSession.user.id,
docsOffline: process.env.DOCAPI_OFFLINE || false
}
};
}
}
(store) => async (ctx) => loadRepresentationPage({ store, ctx })
);
const getDetails = (resultsObj, detailsType) => {
let detailsArr = [];
resultsObj =
detailsType == "mySubmittedReps" ? resultsObj : resultsObj.value;
if (detailsType == "myRepresentations") {
const repsObj = resultsObj.map((searchDetail, index) => {
detailsArr.push(
getCase(searchDetail["incidentID"]).then((data) => {
let caseID = "";
switch (detailsType) {
case "myCases":
caseID = data.pinswg_title;
break;
case "myWatchedCases":
case "mySubmittedReps":
caseID =
data[
"_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
];
break;
case "awaitingSubmission":
case "myRepresentations":
caseID = data.ticketnumber;
break;
default:
caseID = data.pinswg_title;
}
return getPortalModuleDetails(
getFormCollectionByID(data.pinswg_appealcasetype)
.LogicalCollectionName,
caseID
);
})
);
});
}
// const detailsObj = resultsObj.map(async (searchDetail, index) => {
// let caseID = "";
// switch (detailsType) {
// case "myCases":
// caseID = searchDetail.pinswg_title;
// break;
// case "myWatchedCases":
// case "mySubmittedReps":
// caseID =
// searchDetail[
// "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
// ];
// break;
// case "awaitingSubmission":
// caseID = searchDetail.ticketnumber;
// break;
// case "myRepresentations":
// caseID = searchDetail.casereference;
// break;
// default:
// caseID = searchDetail.pinswg_title;
// }
// console.log(detailsType, " case id : ", caseID);
// searchDetail.pinswg_appealcasetype != null &&
// detailsArr.push(
// getPortalModuleDetails(
// getFormCollectionByID(searchDetail.pinswg_appealcasetype)
// .LogicalCollectionName,
// caseID
// // detailsType != "myWatchedCases"
// // ? searchDetail.ticketnumber
// // : searchDetail[
// // "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
// // ]
// )
// );
// });
let detArr = Promise.all(detailsArr);
console.log(detArr);
return detArr;
};
// const mapDispatchToProps = (dispatch) => {
// return {
// setAccountDetails: (loggedInUser) => {