diff --git a/context/representations-refactor-tracker.md b/context/representations-refactor-tracker.md index 6b21cb9b..29ef8a74 100644 --- a/context/representations-refactor-tracker.md +++ b/context/representations-refactor-tracker.md @@ -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 diff --git a/lib/representation/pageLoaders.js b/lib/representation/pageLoaders.js new file mode 100644 index 00000000..cab234ef --- /dev/null +++ b/lib/representation/pageLoaders.js @@ -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 }); +}; diff --git a/pages/myportal/representation.js b/pages/myportal/representation.js index d7a6fabc..2d7b952d 100644 --- a/pages/myportal/representation.js +++ b/pages/myportal/representation.js @@ -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) => {