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:
@@ -0,0 +1,42 @@
|
||||
const RepresentationActionButtons = ({
|
||||
t,
|
||||
locale,
|
||||
isSaving,
|
||||
isContinueDisabled,
|
||||
onSaveExit
|
||||
}) => {
|
||||
return (
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-button-group">
|
||||
<button
|
||||
type="submit"
|
||||
className="govuk-button"
|
||||
data-module="govuk-button"
|
||||
disabled={isContinueDisabled}
|
||||
>
|
||||
{t("common:continue-button")}
|
||||
</button>
|
||||
|
||||
{isSaving ? (
|
||||
<span className=" progress-save-active">
|
||||
{locale == "cy" ? "Nôl data" : "Saving data"}
|
||||
<img
|
||||
className="data-loading-icon"
|
||||
src="/assets/images/loading.gif"
|
||||
alt={locale == "cy" ? "Nôl data" : "Saving data"}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
className="govuk-button progress-save "
|
||||
onClick={onSaveExit}
|
||||
>
|
||||
{t("common:save-exit-button")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RepresentationActionButtons;
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Field } from "redux-form";
|
||||
|
||||
import { RenderPickList } from "../representationElements";
|
||||
|
||||
const RepresentationTypeSelectorBlock = ({
|
||||
t,
|
||||
headingText,
|
||||
kindLabelText,
|
||||
isTypeSelected,
|
||||
selectedTypeDisplay,
|
||||
optionsArr,
|
||||
validate,
|
||||
errorMsg
|
||||
}) => {
|
||||
return (
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-form-group">
|
||||
{!isTypeSelected ? (
|
||||
<>
|
||||
<h2 className="govuk-heading-m ">{headingText}</h2>
|
||||
|
||||
<label
|
||||
className="govuk-label govuk-body-m"
|
||||
htmlFor="representationType"
|
||||
>
|
||||
{kindLabelText}
|
||||
</label>
|
||||
|
||||
<Field
|
||||
name="representationType"
|
||||
component={RenderPickList}
|
||||
datafieldname="representationType"
|
||||
validate={validate}
|
||||
optionsArr={optionsArr}
|
||||
errorMsg={errorMsg}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<label className="govuk-label govuk-body-m">
|
||||
{t("myrepresentations:representation-type")}:{" "}
|
||||
{selectedTypeDisplay}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RepresentationTypeSelectorBlock;
|
||||
@@ -33,6 +33,25 @@ import RepLPACapacitySelection from "./representationLPACapacitySelection";
|
||||
import RepLandOwner from "./representationLandowner";
|
||||
import RepresentationProgress_enforcement from "./representationProgress/representationProgress_enforcement";
|
||||
import RepresentationProgress_s78 from "./representationProgress/representationProgress_s78";
|
||||
import RepresentationFlowFormStage from "./shell/RepresentationFlowFormStage";
|
||||
import {
|
||||
getQuestionnaireNextSection,
|
||||
resolveJourneyStageFlags,
|
||||
resolveRepresentationControlKey,
|
||||
resolveSubmitTransition
|
||||
} from "./utils/stepResolution";
|
||||
import {
|
||||
findDetailsObjForSubmit,
|
||||
findDetailsObjForView,
|
||||
findResultsObjForCurrentReference,
|
||||
resolveInitialCaseDataSources,
|
||||
resolveSubmitCaseDetailsSource
|
||||
} from "./utils/dataResolution";
|
||||
import {
|
||||
buildRepresentationUpdateBody,
|
||||
buildSubmitEnrichedValues,
|
||||
runFinalisationSequence
|
||||
} from "./utils/finalisationBoundary";
|
||||
|
||||
let MakeRepresentation = (props) => {
|
||||
let { t } = useTranslation();
|
||||
@@ -105,6 +124,11 @@ let MakeRepresentation = (props) => {
|
||||
: false;
|
||||
|
||||
const isNRW = currentView.caseReference?.isNRW;
|
||||
const { showCheckStage } = resolveJourneyStageFlags({
|
||||
"representationSubmit": currentView.representationSubmit,
|
||||
"representationSubmitConfirmation":
|
||||
currentView.representationSubmitConfirmation
|
||||
});
|
||||
|
||||
const setRepFileName = (values) => {
|
||||
if (
|
||||
@@ -250,52 +274,31 @@ let MakeRepresentation = (props) => {
|
||||
const isEditState = router.query.state === "edit";
|
||||
const hasState = router.query.hasOwnProperty("state");
|
||||
|
||||
const getCaseDetailsObj = () => {
|
||||
if (hasState) {
|
||||
return isEditState
|
||||
? props.props.myRepresentations.myRepresentations
|
||||
: props.props.searchResultsObj.searchDetailsObj;
|
||||
}
|
||||
return props.currentType === "watchedCases"
|
||||
? props.props.watchedCases.watchedCasesDetails
|
||||
: props.props.searchResultsObj.searchDetailsObj;
|
||||
};
|
||||
const { caseDetailsSource, caseResultsSource } =
|
||||
resolveInitialCaseDataSources({
|
||||
hasState,
|
||||
isEditState,
|
||||
currentType: props.currentType,
|
||||
myRepresentations: props.props.myRepresentations.myRepresentations,
|
||||
searchDetailsObj: props.props.searchResultsObj.searchDetailsObj,
|
||||
searchResultsObj: props.props.searchResultsObj.searchResultsObj,
|
||||
watchedCasesDetails: props.props.watchedCases.watchedCasesDetails
|
||||
});
|
||||
|
||||
const getCaseResultsObj = () => {
|
||||
if (hasState) {
|
||||
return isEditState
|
||||
? props.props.myRepresentations.myRepresentations
|
||||
: props.props.searchResultsObj.searchResultsObj;
|
||||
}
|
||||
return props.currentType === "watchedCases"
|
||||
? props.props.watchedCases.watchedCasesDetails
|
||||
: props.props.searchResultsObj.searchResultsObj;
|
||||
};
|
||||
|
||||
const setCaseDetailsObj = getCaseDetailsObj();
|
||||
const setCaseResultsObj = getCaseResultsObj();
|
||||
|
||||
let resultsObj = jsonpath({
|
||||
path: `$..[?(@ && @.ticketnumber=="${currentView.caseReference.currentReference}")]`,
|
||||
json: setCaseResultsObj,
|
||||
eval: true
|
||||
})[0];
|
||||
let resultsObj = findResultsObjForCurrentReference({
|
||||
"currentReference": currentView.caseReference.currentReference,
|
||||
"caseResultsSource": caseResultsSource
|
||||
});
|
||||
|
||||
// let detailsObj;
|
||||
|
||||
if (hasState && isEditState) {
|
||||
detailsObj = jsonpath({
|
||||
path: `$..[?(@ && @.repfile_name=="${router.query.created}")]`,
|
||||
json: setCaseDetailsObj,
|
||||
eval: true
|
||||
})[0];
|
||||
} else {
|
||||
detailsObj = jsonpath({
|
||||
path: `$..[?(@ && @.ticketnumber=="${currentView.caseReference.ticketnumber}")]`,
|
||||
json: setCaseDetailsObj,
|
||||
eval: true
|
||||
})[0];
|
||||
}
|
||||
detailsObj = findDetailsObjForView({
|
||||
hasState,
|
||||
isEditState,
|
||||
"queryCreated": router.query.created,
|
||||
"ticketnumber": currentView.caseReference.ticketnumber,
|
||||
"caseDetailsSource": caseDetailsSource
|
||||
});
|
||||
|
||||
const safeDetailsObj = detailsObj || {};
|
||||
const safeResultsObj = resultsObj || {};
|
||||
@@ -586,19 +589,16 @@ let MakeRepresentation = (props) => {
|
||||
setCurrentReference
|
||||
};
|
||||
|
||||
const getNormalizedCapacity = () => {
|
||||
const rawCapacity =
|
||||
currentView.representationCapacity ||
|
||||
currentView.caseReference?.repDetails?.representationCapacity ||
|
||||
"false";
|
||||
return rawCapacity.toLowerCase();
|
||||
};
|
||||
const { controlKey } = resolveRepresentationControlKey({
|
||||
"isLPA": isLPA,
|
||||
"representationCapacity": currentView.representationCapacity,
|
||||
"repDetailsCapacity":
|
||||
currentView.caseReference?.repDetails?.representationCapacity,
|
||||
"appealType": appealType,
|
||||
"normalizeCapacity": normalizeCapacity
|
||||
});
|
||||
|
||||
if (
|
||||
isLPA &&
|
||||
(!currentView.representationCapacity ||
|
||||
currentView.representationCapacity === "")
|
||||
) {
|
||||
if (controlKey === "lpa-capacity-selection") {
|
||||
return (
|
||||
<RepLPACapacitySelection
|
||||
{...commonProps}
|
||||
@@ -616,10 +616,8 @@ let MakeRepresentation = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
const capacity = normalizeCapacity(getNormalizedCapacity());
|
||||
|
||||
switch (capacity) {
|
||||
case "appellant": {
|
||||
switch (controlKey) {
|
||||
case "capacity-appellant": {
|
||||
const Component =
|
||||
appealType === 846040002
|
||||
? ConsultationAppellant
|
||||
@@ -634,7 +632,7 @@ let MakeRepresentation = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
case "lpa": {
|
||||
case "capacity-lpa": {
|
||||
return (
|
||||
<RepLPA
|
||||
{...commonProps}
|
||||
@@ -650,7 +648,7 @@ let MakeRepresentation = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
case "agent": {
|
||||
case "capacity-agent": {
|
||||
const Component =
|
||||
appealType === 846040002 ? ConsultationAgent : RepAgent;
|
||||
return (
|
||||
@@ -663,7 +661,7 @@ let MakeRepresentation = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
case "interestedparty": {
|
||||
case "capacity-interestedparty": {
|
||||
const Component =
|
||||
appealType === 846040002
|
||||
? ConsultationInterestedPartyPerson
|
||||
@@ -678,7 +676,7 @@ let MakeRepresentation = (props) => {
|
||||
);
|
||||
}
|
||||
|
||||
case "landowner": {
|
||||
case "capacity-landowner": {
|
||||
return (
|
||||
<RepLandOwner
|
||||
{...commonProps}
|
||||
@@ -879,24 +877,10 @@ let MakeRepresentation = (props) => {
|
||||
]
|
||||
};
|
||||
|
||||
let updateBody = setRepFileName(formattedValues);
|
||||
|
||||
// Clean and sanitize the body
|
||||
const sanitizedBody = Object.entries(updateBody).reduce(
|
||||
(acc, [key, val]) => {
|
||||
if (val !== null && val !== undefined && !key.startsWith("_")) {
|
||||
acc[key] = val;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
// Normalize "Yes"/"No" to booleans
|
||||
const stringified = JSON.stringify(sanitizedBody)
|
||||
.replace(/:"Yes"/g, ":true")
|
||||
.replace(/:"No"/g, ":false");
|
||||
updateBody = JSON.parse(stringified);
|
||||
const updateBody = buildRepresentationUpdateBody({
|
||||
formattedValues,
|
||||
setRepFileName
|
||||
});
|
||||
|
||||
const sendEmailIfNeeded = () => {
|
||||
if (sendSavedEmail) {
|
||||
@@ -957,24 +941,20 @@ let MakeRepresentation = (props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine case data source
|
||||
const getCaseDetailsSource = () => {
|
||||
if (router.query?.state === "edit") {
|
||||
return props.props.myRepresentations.myRepresentations;
|
||||
}
|
||||
return props.currentType === "watchedCases"
|
||||
? props.props.watchedCases.watchedCasesDetails
|
||||
: props.props.searchResultsObj.searchDetailsObj;
|
||||
};
|
||||
|
||||
const setCaseDetailsObj = getCaseDetailsSource();
|
||||
const caseDetailsSource = resolveSubmitCaseDetailsSource({
|
||||
"isEditState": router.query?.state === "edit",
|
||||
"currentType": props.currentType,
|
||||
"myRepresentations":
|
||||
props.props.myRepresentations.myRepresentations,
|
||||
"watchedCasesDetails": props.props.watchedCases.watchedCasesDetails,
|
||||
"searchDetailsObj": props.props.searchResultsObj.searchDetailsObj
|
||||
});
|
||||
|
||||
// Find correct case details
|
||||
let detailsObj = jsonpath({
|
||||
path: `$..[?(@ && @.ticketnumber=="${ticketnumber}")]`,
|
||||
json: setCaseDetailsObj,
|
||||
eval: true
|
||||
})[0];
|
||||
let detailsObj = findDetailsObjForSubmit({
|
||||
ticketnumber,
|
||||
caseDetailsSource
|
||||
});
|
||||
|
||||
// Cancel submit status if editing
|
||||
if (router.query?.state === "edit") {
|
||||
@@ -988,23 +968,6 @@ let MakeRepresentation = (props) => {
|
||||
) {
|
||||
const formValues = formObj.representationForm.values;
|
||||
|
||||
const buildSiteAddress = () => {
|
||||
const {
|
||||
pinswg_siteaddressline1,
|
||||
pinswg_siteaddressline2,
|
||||
pinswg_siteaddresstown,
|
||||
pinswg_siteaddresspostcode
|
||||
} = detailsObj;
|
||||
return [
|
||||
pinswg_siteaddressline1,
|
||||
pinswg_siteaddressline2,
|
||||
pinswg_siteaddresstown,
|
||||
pinswg_siteaddresspostcode
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
const representationCapacity =
|
||||
router.query?.state === "edit"
|
||||
? props.props.currentView.representationCapacity
|
||||
@@ -1012,55 +975,19 @@ let MakeRepresentation = (props) => {
|
||||
? "LPA"
|
||||
: values.representationCapacity;
|
||||
|
||||
Object.assign(values, {
|
||||
...formValues,
|
||||
containerID: props.props.accountDetails.containerID,
|
||||
appealType:
|
||||
appealType || caseReferenceObj.repDetails.appealType,
|
||||
incidentID:
|
||||
caseReferenceObj.incidentid ||
|
||||
caseReferenceObj.repDetails.incidentid,
|
||||
caseRef: caseReferenceObj.currentReference,
|
||||
casereference: caseReferenceObj.currentReference,
|
||||
pinswg_questionnaireduedate:
|
||||
detailsObj.pinswg_questionnaireduedate,
|
||||
pinswg_endofrepresentationperiod:
|
||||
detailsObj.pinswg_endofrepresentationperiod,
|
||||
pinswg_applicationacceptedasvalid:
|
||||
detailsObj.pinswg_applicationacceptedasvalid,
|
||||
pinswg_statementduedate: detailsObj.pinswg_statementduedate,
|
||||
pinswg_statementsduedate: detailsObj.pinswg_statementsduedate,
|
||||
pinswg_finalcommentsduedate:
|
||||
detailsObj.pinswg_finalcommentsduedate,
|
||||
pinswg_name: caseReferenceObj.currentReference,
|
||||
firstname: accountDetails.firstname,
|
||||
lastname: accountDetails.lastname,
|
||||
emailAddress: accountDetails.emailaddress1,
|
||||
siteAddress: buildSiteAddress(),
|
||||
pinswg_siteaddressline1: detailsObj.pinswg_siteaddressline1,
|
||||
pinswg_siteaddressline2: detailsObj.pinswg_siteaddressline2,
|
||||
pinswg_siteaddresstown: detailsObj.pinswg_siteaddresstown,
|
||||
pinswg_siteaddresspostcode:
|
||||
detailsObj.pinswg_siteaddresspostcode,
|
||||
pinswg_lpareference: resultsObj?.pinswg_lpareference,
|
||||
_pinswg_appellant_value: detailsObj._pinswg_appellant_value,
|
||||
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue":
|
||||
detailsObj[
|
||||
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue"
|
||||
],
|
||||
_pinswg_localplanningauthority_value:
|
||||
detailsObj._pinswg_localplanningauthority_value ||
|
||||
detailsObj._pinswg_associatedlpa_value,
|
||||
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue":
|
||||
detailsObj[
|
||||
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue"
|
||||
] ||
|
||||
detailsObj[
|
||||
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
|
||||
],
|
||||
representationCapacity,
|
||||
locale: router.locale
|
||||
});
|
||||
Object.assign(
|
||||
values,
|
||||
buildSubmitEnrichedValues({
|
||||
formValues,
|
||||
accountDetails,
|
||||
appealType,
|
||||
caseReferenceObj,
|
||||
detailsObj,
|
||||
resultsObj,
|
||||
representationCapacity,
|
||||
locale: router.locale
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const shouldSubmit = !currentView.representationSubmit;
|
||||
@@ -1078,19 +1005,20 @@ let MakeRepresentation = (props) => {
|
||||
: normalizeCapacity(values.representationCapacity);
|
||||
setRepresentationCapacity(capacity);
|
||||
} else {
|
||||
if (repType === "Questionnaire") {
|
||||
if (
|
||||
questionnaireCount !== 0 &&
|
||||
showQuestionnaireSection < questionnaireCount
|
||||
) {
|
||||
setShowQuestionnaireSection(
|
||||
showQuestionnaireSection + 1
|
||||
);
|
||||
updateRepresentation(values, false, false);
|
||||
} else {
|
||||
updateRepresentation(values, false, false);
|
||||
setRepresentationSubmit(true);
|
||||
}
|
||||
const submitTransition = resolveSubmitTransition({
|
||||
"representationType": repType,
|
||||
"showQuestionnaireSection": showQuestionnaireSection,
|
||||
"questionnaireCount": questionnaireCount
|
||||
});
|
||||
|
||||
if (submitTransition === "advanceQuestionnaire") {
|
||||
setShowQuestionnaireSection(
|
||||
getQuestionnaireNextSection(
|
||||
showQuestionnaireSection,
|
||||
questionnaireCount
|
||||
)
|
||||
);
|
||||
updateRepresentation(values, false, false);
|
||||
} else {
|
||||
updateRepresentation(values, false, false);
|
||||
setRepresentationSubmit(true);
|
||||
@@ -1113,19 +1041,19 @@ let MakeRepresentation = (props) => {
|
||||
}
|
||||
|
||||
// Final PDF & upload
|
||||
setFinaliseAppealProcess(true);
|
||||
generateRepPDF(
|
||||
runFinalisationSequence({
|
||||
values,
|
||||
props.props.accountDetails.containerID,
|
||||
props.currentView.caseReference.currentReference
|
||||
).then((data) => {
|
||||
containerID: props.props.accountDetails.containerID,
|
||||
currentReference:
|
||||
props.currentView.caseReference.currentReference,
|
||||
setFinaliseAppealProcess,
|
||||
generateRepPDF,
|
||||
updateLinks,
|
||||
uploadRepresentationFiles,
|
||||
setRepresentationSubmit,
|
||||
setRepresentationSubmitConfirmation
|
||||
}).then((data) => {
|
||||
console.log(data, "");
|
||||
if (data.status === "success") {
|
||||
values = updateLinks(values);
|
||||
uploadRepresentationFiles(values);
|
||||
setRepresentationSubmit(true);
|
||||
setRepresentationSubmitConfirmation(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1252,7 +1180,7 @@ let MakeRepresentation = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={handleSubmit(onHandleSubmit)}>
|
||||
{currentView.representationSubmit ? (
|
||||
{showCheckStage ? (
|
||||
<RepCompleteSubmit
|
||||
formObj={formObj}
|
||||
capacityOptionsArr={capacityOptionsArr}
|
||||
@@ -1279,61 +1207,22 @@ let MakeRepresentation = (props) => {
|
||||
docsOffline={docsOffline}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<h1 className="govuk-heading-l">
|
||||
{appealIsConsultation
|
||||
? t(
|
||||
"myrepresentations:make-a-consultation-on-label"
|
||||
)
|
||||
: t(
|
||||
"myrepresentations:make-a-representation-on-label"
|
||||
)}{" "}
|
||||
{currentView.caseReference.currentReference}{" "}
|
||||
</h1>
|
||||
<div id="rep-form">
|
||||
<div className="govuk-grid-column-three-quarters wgForm">
|
||||
{whichControl()}
|
||||
</div>
|
||||
|
||||
<div className="govuk-grid-column-one-quarter">
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<h2 className="govuk-heading-s">
|
||||
{t(
|
||||
"myrepresentations:timetable-title"
|
||||
)}
|
||||
</h2>
|
||||
<div className="govuk-summary-list">
|
||||
<div className="govuk-body-s">
|
||||
{appealIsLocalImpact
|
||||
? t(
|
||||
"myrepresentations:local-impact-report"
|
||||
)
|
||||
: t(
|
||||
"myrepresentations:questionnaire-due-label"
|
||||
)}
|
||||
:{" "}
|
||||
<span className="govuk-body-s">
|
||||
{renderDueDate()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{!appealIsConsultation &&
|
||||
renderStatementsDue()}
|
||||
|
||||
{renderMarineImpact()}
|
||||
|
||||
{renderConsultationClose()}
|
||||
|
||||
{renderFinalCommentsDue()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isQuestionnaire && whichProgress()}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
<RepresentationFlowFormStage
|
||||
t={t}
|
||||
appealIsConsultation={appealIsConsultation}
|
||||
currentReference={
|
||||
currentView.caseReference.currentReference
|
||||
}
|
||||
whichControl={whichControl}
|
||||
appealIsLocalImpact={appealIsLocalImpact}
|
||||
renderDueDate={renderDueDate}
|
||||
renderStatementsDue={renderStatementsDue}
|
||||
renderMarineImpact={renderMarineImpact}
|
||||
renderConsultationClose={renderConsultationClose}
|
||||
renderFinalCommentsDue={renderFinalCommentsDue}
|
||||
isQuestionnaire={isQuestionnaire}
|
||||
whichProgress={whichProgress}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -8,16 +8,17 @@ import { bytesToSize, getThumbnailIconByExtension } from "../../utils";
|
||||
import {
|
||||
FileUploadField,
|
||||
RenderCondtionalRadioList,
|
||||
RenderPickList,
|
||||
RenderRichMultiline,
|
||||
shouldDisableButton,
|
||||
shouldDisableButton
|
||||
} from "./representationElements";
|
||||
import RepresentationActionButtons from "./elements/RepresentationActionButtons";
|
||||
import RepresentationTypeSelectorBlock from "./elements/RepresentationTypeSelectorBlock";
|
||||
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import {
|
||||
setRepresentationCapacity,
|
||||
setRepresentationSubmit,
|
||||
setFilesForRepresentations,
|
||||
setFilesForRepresentations
|
||||
} from "../../../store/currentView/action";
|
||||
|
||||
const RepAgent = (props) => {
|
||||
@@ -35,7 +36,7 @@ const RepAgent = (props) => {
|
||||
updateRepresentation,
|
||||
setSavingStatus,
|
||||
savingStatus,
|
||||
setFilesForRepresentations,
|
||||
setFilesForRepresentations
|
||||
} = props;
|
||||
const { acceptedFiles, getRootProps, getInputProps } = useDropzone();
|
||||
|
||||
@@ -63,58 +64,44 @@ const RepAgent = (props) => {
|
||||
));
|
||||
const required = (value) => (value ? undefined : "Required");
|
||||
|
||||
const hasTypeSelectionInline =
|
||||
typeof formObj.representationForm.values.representationType ==
|
||||
"undefined" && props.representationType != "Select...";
|
||||
|
||||
const selectedRepresentationTypeDisplay =
|
||||
router.locale == "cy"
|
||||
? formObj.representationForm.values.representationType ==
|
||||
"Questionnaire"
|
||||
? "Holiadur"
|
||||
: formObj.representationForm.values.representationType ==
|
||||
"Statement"
|
||||
? "Datganiad"
|
||||
: formObj.representationForm.values.representationType ==
|
||||
"Final comments"
|
||||
? "Sylwadau terfynol"
|
||||
: formObj.representationForm.values.representationType
|
||||
: formObj.representationForm.values.representationType;
|
||||
|
||||
const handleSaveExit = () => {
|
||||
let valuesObj = props.props.props.form[props.form].values || {};
|
||||
|
||||
delete valuesObj["_pinswg_appellant_value"];
|
||||
|
||||
updateRepresentation(valuesObj, false, true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-form-group">
|
||||
{typeof formObj.representationForm.values
|
||||
.representationType == "undefined" &&
|
||||
props.representationType != "Select..." ? (
|
||||
<>
|
||||
<h2 className="govuk-heading-m ">
|
||||
{t(
|
||||
"myrepresentations:representation-from-an-agent-heading"
|
||||
)}
|
||||
</h2>
|
||||
|
||||
<label
|
||||
className="govuk-label govuk-body-m"
|
||||
htmlFor="representationType"
|
||||
>
|
||||
{t("myrepresentations:kind-of-rep-label")}
|
||||
</label>
|
||||
|
||||
<Field
|
||||
name="representationType"
|
||||
component={RenderPickList}
|
||||
datafieldname="representationType"
|
||||
optionsArr={
|
||||
appellantApplicantRepresentationArr
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<label className="govuk-label govuk-body-m">
|
||||
{t("myrepresentations:representation-type")}:{" "}
|
||||
{router.locale == "cy"
|
||||
? formObj.representationForm.values
|
||||
.representationType == "Questionnaire"
|
||||
? "Holiadur"
|
||||
: formObj.representationForm.values
|
||||
.representationType == "Statement"
|
||||
? "Datganiad"
|
||||
: formObj.representationForm.values
|
||||
.representationType ==
|
||||
"Final comments"
|
||||
? "Sylwadau terfynol"
|
||||
: formObj.representationForm.values
|
||||
.representationType
|
||||
: formObj.representationForm.values
|
||||
.representationType}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RepresentationTypeSelectorBlock
|
||||
t={t}
|
||||
headingText={t(
|
||||
"myrepresentations:representation-from-an-agent-heading"
|
||||
)}
|
||||
kindLabelText={t("myrepresentations:kind-of-rep-label")}
|
||||
isTypeSelected={!hasTypeSelectionInline}
|
||||
selectedTypeDisplay={selectedRepresentationTypeDisplay}
|
||||
optionsArr={appellantApplicantRepresentationArr}
|
||||
/>
|
||||
{(typeof props.representationType != "undefined" ||
|
||||
typeof formObj.representationForm.values
|
||||
.representationType != "undefined") && (
|
||||
@@ -242,62 +229,16 @@ const RepAgent = (props) => {
|
||||
)}
|
||||
{typeof formObj.representationForm.values.representationType !=
|
||||
"undefined" && (
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-button-group">
|
||||
<button
|
||||
type="submit"
|
||||
className="govuk-button"
|
||||
data-module="govuk-button"
|
||||
disabled={shouldDisableButton({
|
||||
props,
|
||||
formObj,
|
||||
})}
|
||||
>
|
||||
{t("common:continue-button")}
|
||||
</button>
|
||||
|
||||
{savingStatus ? (
|
||||
<span className=" progress-save-active">
|
||||
{router.locale == "cy"
|
||||
? "Nôl data"
|
||||
: "Saving data"}
|
||||
<img
|
||||
className="data-loading-icon"
|
||||
src="/assets/images/loading.gif"
|
||||
alt={
|
||||
router.locale == "cy"
|
||||
? "Nôl data"
|
||||
: "Saving data"
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
className="govuk-button progress-save "
|
||||
onClick={() => {
|
||||
let valuesObj =
|
||||
props.props.props.form[props.form]
|
||||
.values || {};
|
||||
|
||||
//console.log("valuesobj:", valuesObj);
|
||||
|
||||
delete valuesObj[
|
||||
"_pinswg_appellant_value"
|
||||
];
|
||||
|
||||
//console.log("on save:", valuesObj);
|
||||
updateRepresentation(
|
||||
valuesObj,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("common:save-exit-button")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RepresentationActionButtons
|
||||
t={t}
|
||||
locale={router.locale}
|
||||
isSaving={savingStatus}
|
||||
isContinueDisabled={shouldDisableButton({
|
||||
props,
|
||||
formObj
|
||||
})}
|
||||
onSaveExit={handleSaveExit}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -310,7 +251,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
setFilesForRepresentations: (fileList) => {
|
||||
dispatch(setFilesForRepresentations(fileList));
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
@@ -320,6 +261,6 @@ export default connect(
|
||||
reduxForm({
|
||||
form: "representationForm",
|
||||
enableReinitialize: true,
|
||||
destroyOnUnmount: false,
|
||||
destroyOnUnmount: false
|
||||
})(RepAgent)
|
||||
);
|
||||
|
||||
@@ -9,14 +9,16 @@ import {
|
||||
FileUploadField,
|
||||
RenderPickList,
|
||||
RenderRichMultiline,
|
||||
shouldDisableButton,
|
||||
shouldDisableButton
|
||||
} from "./representationElements";
|
||||
import RepresentationActionButtons from "./elements/RepresentationActionButtons";
|
||||
import RepresentationTypeSelectorBlock from "./elements/RepresentationTypeSelectorBlock";
|
||||
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import {
|
||||
setRepresentationCapacity,
|
||||
setRepresentationSubmit,
|
||||
setFilesForRepresentations,
|
||||
setFilesForRepresentations
|
||||
} from "../../../store/currentView/action";
|
||||
|
||||
const RepAppellant = (props) => {
|
||||
@@ -34,7 +36,7 @@ const RepAppellant = (props) => {
|
||||
updateRepresentation,
|
||||
setSavingStatus,
|
||||
savingStatus,
|
||||
setFilesForRepresentations,
|
||||
setFilesForRepresentations
|
||||
} = props;
|
||||
const { acceptedFiles, getRootProps, getInputProps } = useDropzone();
|
||||
|
||||
@@ -62,56 +64,44 @@ const RepAppellant = (props) => {
|
||||
index === self.findIndex((t) => t.name === value.name)
|
||||
);
|
||||
|
||||
const hasTypeSelectionInline =
|
||||
typeof formObj.representationForm.values.representationType ==
|
||||
"undefined" && props.representationType != "Select...";
|
||||
|
||||
const selectedRepresentationTypeDisplay =
|
||||
router.locale == "cy"
|
||||
? formObj.representationForm.values.representationType ==
|
||||
"Questionnaire"
|
||||
? "Holiadur"
|
||||
: formObj.representationForm.values.representationType ==
|
||||
"Statement"
|
||||
? "Datganiad"
|
||||
: formObj.representationForm.values.representationType ==
|
||||
"Final comments"
|
||||
? "Sylwadau terfynol"
|
||||
: formObj.representationForm.values.representationType
|
||||
: formObj.representationForm.values.representationType;
|
||||
|
||||
const handleSaveExit = () => {
|
||||
let valuesObj = props.props.props.form[props.form].values || {};
|
||||
|
||||
delete valuesObj["_pinswg_appellant_value"];
|
||||
|
||||
updateRepresentation(valuesObj, false, true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-form-group">
|
||||
{typeof formObj.representationForm.values
|
||||
.representationType == "undefined" &&
|
||||
props.representationType != "Select..." ? (
|
||||
<>
|
||||
<h2 className="govuk-heading-m ">
|
||||
{t(
|
||||
"myrepresentations:representation-from-an-appellant-heading"
|
||||
)}
|
||||
</h2>
|
||||
<label
|
||||
className="govuk-label govuk-body-m"
|
||||
htmlFor="representationType"
|
||||
>
|
||||
{t("myrepresentations:kind-of-rep-label")}
|
||||
</label>
|
||||
<Field
|
||||
name="representationType"
|
||||
component={RenderPickList}
|
||||
datafieldname="representationType"
|
||||
optionsArr={
|
||||
appellantApplicantRepresentationArr
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<label className="govuk-label govuk-body-m">
|
||||
{t("myrepresentations:representation-type")}:{" "}
|
||||
{router.locale == "cy"
|
||||
? formObj.representationForm.values
|
||||
.representationType == "Questionnaire"
|
||||
? "Holiadur"
|
||||
: formObj.representationForm.values
|
||||
.representationType == "Statement"
|
||||
? "Datganiad"
|
||||
: formObj.representationForm.values
|
||||
.representationType ==
|
||||
"Final comments"
|
||||
? "Sylwadau terfynol"
|
||||
: formObj.representationForm.values
|
||||
.representationType
|
||||
: formObj.representationForm.values
|
||||
.representationType}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RepresentationTypeSelectorBlock
|
||||
t={t}
|
||||
headingText={t(
|
||||
"myrepresentations:representation-from-an-appellant-heading"
|
||||
)}
|
||||
kindLabelText={t("myrepresentations:kind-of-rep-label")}
|
||||
isTypeSelected={!hasTypeSelectionInline}
|
||||
selectedTypeDisplay={selectedRepresentationTypeDisplay}
|
||||
optionsArr={appellantApplicantRepresentationArr}
|
||||
/>
|
||||
{(typeof props.representationType != "undefined" ||
|
||||
typeof formObj.representationForm.values
|
||||
.representationType != "undefined") && (
|
||||
@@ -232,62 +222,16 @@ const RepAppellant = (props) => {
|
||||
|
||||
{typeof formObj.representationForm.values.representationType !=
|
||||
"undefined" && (
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-button-group">
|
||||
<button
|
||||
type="submit"
|
||||
className="govuk-button"
|
||||
data-module="govuk-button"
|
||||
disabled={shouldDisableButton({
|
||||
props,
|
||||
formObj,
|
||||
})}
|
||||
>
|
||||
{t("common:continue-button")}
|
||||
</button>
|
||||
|
||||
{savingStatus ? (
|
||||
<span className=" progress-save-active">
|
||||
{router.locale == "cy"
|
||||
? "Nôl data"
|
||||
: "Saving data"}
|
||||
<img
|
||||
className="data-loading-icon"
|
||||
src="/assets/images/loading.gif"
|
||||
alt={
|
||||
router.locale == "cy"
|
||||
? "Nôl data"
|
||||
: "Saving data"
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
className="govuk-button progress-save "
|
||||
onClick={() => {
|
||||
let valuesObj =
|
||||
props.props.props.form[props.form]
|
||||
.values || {};
|
||||
|
||||
//console.log("valuesobj:", valuesObj);
|
||||
|
||||
delete valuesObj[
|
||||
"_pinswg_appellant_value"
|
||||
];
|
||||
|
||||
//console.log("on save:", valuesObj);
|
||||
updateRepresentation(
|
||||
valuesObj,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("common:save-exit-button")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RepresentationActionButtons
|
||||
t={t}
|
||||
locale={router.locale}
|
||||
isSaving={savingStatus}
|
||||
isContinueDisabled={shouldDisableButton({
|
||||
props,
|
||||
formObj
|
||||
})}
|
||||
onSaveExit={handleSaveExit}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
@@ -303,7 +247,7 @@ const mapDispatchToProps = (dispatch) => {
|
||||
|
||||
setFilesForRepresentations: (fileList) => {
|
||||
dispatch(setFilesForRepresentations(fileList));
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -314,6 +258,6 @@ export default connect(
|
||||
reduxForm({
|
||||
form: "representationForm",
|
||||
enableReinitialize: true,
|
||||
destroyOnUnmount: false,
|
||||
destroyOnUnmount: false
|
||||
})(RepAppellant)
|
||||
);
|
||||
|
||||
@@ -61,6 +61,13 @@ const RepCompleteSubmit = (props) => {
|
||||
|
||||
repFormData = updateLinks(repFormData);
|
||||
|
||||
const representationOnBehalfOfDisplay =
|
||||
repFormData.representationOnBehalfOf === "Yes"
|
||||
? t("myrepresentations:questionnaire-yes")
|
||||
: repFormData.representationOnBehalfOf === "No"
|
||||
? t("myrepresentations:questionnaire-no")
|
||||
: repFormData.representationOnBehalfOf;
|
||||
|
||||
//const repDate = new Date();
|
||||
|
||||
// let day = repDate.getDate();
|
||||
@@ -406,7 +413,7 @@ const RepCompleteSubmit = (props) => {
|
||||
|
||||
<dd className="govuk-summary-list__value">
|
||||
{
|
||||
repFormData.representationOnBehalfOf
|
||||
representationOnBehalfOfDisplay
|
||||
}
|
||||
</dd>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -8,10 +8,11 @@ import { bytesToSize, getThumbnailIconByExtension } from "../../utils";
|
||||
import {
|
||||
FileUploadField,
|
||||
RenderCondtionalRadioList,
|
||||
RenderPickList,
|
||||
RenderRichMultiline,
|
||||
shouldDisableButton
|
||||
} from "./representationElements";
|
||||
import RepresentationActionButtons from "./elements/RepresentationActionButtons";
|
||||
import RepresentationTypeSelectorBlock from "./elements/RepresentationTypeSelectorBlock";
|
||||
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import {
|
||||
@@ -62,63 +63,47 @@ const RepInterestedPartyPerson = (props) => {
|
||||
{file.path} - {file.size} bytes
|
||||
</li>
|
||||
));
|
||||
|
||||
const hasTypeSelectionInline =
|
||||
typeof formObj.representationForm.values.representationType ==
|
||||
"undefined" && props.representationType != "Select...";
|
||||
|
||||
const selectedRepresentationTypeDisplay =
|
||||
router.locale == "cy"
|
||||
? formObj.representationForm.values.representationType ==
|
||||
"Questionnaire"
|
||||
? "Holiadur"
|
||||
: formObj.representationForm.values.representationType ==
|
||||
"Statement"
|
||||
? "Datganiad"
|
||||
: formObj.representationForm.values.representationType ==
|
||||
"Final comments"
|
||||
? "Sylwadau terfynol"
|
||||
: formObj.representationForm.values.representationType
|
||||
: formObj.representationForm.values.representationType;
|
||||
|
||||
const handleSaveExit = () => {
|
||||
let valuesObj = props.props.props.form[props.form].values || {};
|
||||
|
||||
delete valuesObj["_pinswg_appellant_value"];
|
||||
|
||||
updateRepresentation(valuesObj, false, true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-form-group">
|
||||
{typeof formObj.representationForm.values
|
||||
.representationType == "undefined" &&
|
||||
props.representationType != "Select..." ? (
|
||||
<>
|
||||
<h2 className="govuk-heading-m ">
|
||||
{t(
|
||||
"myrepresentations:consultation-from-an-interested-person-heading"
|
||||
)}
|
||||
</h2>
|
||||
|
||||
<label
|
||||
className="govuk-label govuk-body-m"
|
||||
htmlFor="representationType"
|
||||
>
|
||||
{t("myrepresentations:kind-of-rep-label")}
|
||||
</label>
|
||||
|
||||
<Field
|
||||
name="representationType"
|
||||
component={RenderPickList}
|
||||
datafieldname="representationType"
|
||||
validate={[required]}
|
||||
optionsArr={
|
||||
interestedPersonRepresentationArr
|
||||
}
|
||||
errorMsg={t(
|
||||
"myrepresentations:select-an-option-label"
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<label className="govuk-label govuk-body-m">
|
||||
{t("myrepresentations:representation-type")}:{" "}
|
||||
{router.locale == "cy"
|
||||
? formObj.representationForm.values
|
||||
.representationType == "Questionnaire"
|
||||
? "Holiadur"
|
||||
: formObj.representationForm.values
|
||||
.representationType ==
|
||||
"Statement"
|
||||
? "Datganiad"
|
||||
: formObj.representationForm.values
|
||||
.representationType ==
|
||||
"Final comments"
|
||||
? "Sylwadau terfynol"
|
||||
: formObj.representationForm.values
|
||||
.representationType
|
||||
: formObj.representationForm.values
|
||||
.representationType}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RepresentationTypeSelectorBlock
|
||||
t={t}
|
||||
headingText={t(
|
||||
"myrepresentations:consultation-from-an-interested-person-heading"
|
||||
)}
|
||||
kindLabelText={t("myrepresentations:kind-of-rep-label")}
|
||||
isTypeSelected={!hasTypeSelectionInline}
|
||||
selectedTypeDisplay={selectedRepresentationTypeDisplay}
|
||||
optionsArr={interestedPersonRepresentationArr}
|
||||
validate={[required]}
|
||||
errorMsg={t("myrepresentations:select-an-option-label")}
|
||||
/>
|
||||
{(typeof props.representationType != "undefined" ||
|
||||
typeof formObj.representationForm.values
|
||||
.representationType != "undefined") && (
|
||||
@@ -129,12 +114,7 @@ const RepInterestedPartyPerson = (props) => {
|
||||
name="representationOnBehalfOf"
|
||||
datafieldname="representationOnBehalfOf"
|
||||
// label="In what capacity do you wish to make representations on this case?"
|
||||
options={[
|
||||
t(
|
||||
"myrepresentations:questionnaire-yes"
|
||||
),
|
||||
t("myrepresentations:questionnaire-no")
|
||||
]}
|
||||
options={["Yes", "No"]}
|
||||
id="representationOnBehalfOf"
|
||||
className="govuk-radios__input"
|
||||
errorMsg={t(
|
||||
@@ -235,80 +215,16 @@ const RepInterestedPartyPerson = (props) => {
|
||||
)}
|
||||
{typeof formObj.representationForm.values.representationType !=
|
||||
"undefined" && (
|
||||
<div className="govuk-grid-row">
|
||||
<div className="govuk-button-group">
|
||||
<button
|
||||
type="submit"
|
||||
className="govuk-button"
|
||||
data-module="govuk-button"
|
||||
disabled={shouldDisableButton({
|
||||
props,
|
||||
formObj
|
||||
})}
|
||||
>
|
||||
{t("common:continue-button")}
|
||||
</button>
|
||||
{/*
|
||||
<a
|
||||
onClick={() => {
|
||||
setRepresentationCapacity();
|
||||
props.updateField(
|
||||
"representationForm",
|
||||
"representationCapacity",
|
||||
""
|
||||
);
|
||||
props.updateField(
|
||||
"representationForm",
|
||||
"representationType",
|
||||
""
|
||||
);
|
||||
}}
|
||||
className="govuk-link"
|
||||
>
|
||||
{t("case:summary-back-button-label")}
|
||||
</a> */}
|
||||
{savingStatus ? (
|
||||
<span className=" progress-save-active">
|
||||
{router.locale == "cy"
|
||||
? "Nôl data"
|
||||
: "Saving data"}
|
||||
<img
|
||||
className="data-loading-icon"
|
||||
src="/assets/images/loading.gif"
|
||||
alt={
|
||||
router.locale == "cy"
|
||||
? "Nôl data"
|
||||
: "Saving data"
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<button
|
||||
className="govuk-button progress-save "
|
||||
onClick={() => {
|
||||
let valuesObj =
|
||||
props.props.props.form[props.form]
|
||||
.values || {};
|
||||
|
||||
//console.log("valuesobj:", valuesObj);
|
||||
|
||||
delete valuesObj[
|
||||
"_pinswg_appellant_value"
|
||||
];
|
||||
|
||||
//console.log("on save:", valuesObj);
|
||||
updateRepresentation(
|
||||
valuesObj,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("common:save-exit-button")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<RepresentationActionButtons
|
||||
t={t}
|
||||
locale={router.locale}
|
||||
isSaving={savingStatus}
|
||||
isContinueDisabled={shouldDisableButton({
|
||||
props,
|
||||
formObj
|
||||
})}
|
||||
onSaveExit={handleSaveExit}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import RepresentationTimetablePanel from "./RepresentationTimetablePanel";
|
||||
|
||||
const RepresentationFlowFormStage = ({
|
||||
t,
|
||||
appealIsConsultation,
|
||||
currentReference,
|
||||
whichControl,
|
||||
appealIsLocalImpact,
|
||||
renderDueDate,
|
||||
renderStatementsDue,
|
||||
renderMarineImpact,
|
||||
renderConsultationClose,
|
||||
renderFinalCommentsDue,
|
||||
isQuestionnaire,
|
||||
whichProgress
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<h1 className="govuk-heading-l">
|
||||
{appealIsConsultation
|
||||
? t("myrepresentations:make-a-consultation-on-label")
|
||||
: t(
|
||||
"myrepresentations:make-a-representation-on-label"
|
||||
)}{" "}
|
||||
{currentReference}{" "}
|
||||
</h1>
|
||||
|
||||
<div id="rep-form">
|
||||
<div className="govuk-grid-column-three-quarters wgForm">
|
||||
{whichControl()}
|
||||
</div>
|
||||
|
||||
<RepresentationTimetablePanel
|
||||
t={t}
|
||||
appealIsConsultation={appealIsConsultation}
|
||||
appealIsLocalImpact={appealIsLocalImpact}
|
||||
renderDueDate={renderDueDate}
|
||||
renderStatementsDue={renderStatementsDue}
|
||||
renderMarineImpact={renderMarineImpact}
|
||||
renderConsultationClose={renderConsultationClose}
|
||||
renderFinalCommentsDue={renderFinalCommentsDue}
|
||||
isQuestionnaire={isQuestionnaire}
|
||||
whichProgress={whichProgress}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RepresentationFlowFormStage;
|
||||
@@ -0,0 +1,49 @@
|
||||
const RepresentationTimetablePanel = ({
|
||||
t,
|
||||
appealIsConsultation,
|
||||
appealIsLocalImpact,
|
||||
renderDueDate,
|
||||
renderStatementsDue,
|
||||
renderMarineImpact,
|
||||
renderConsultationClose,
|
||||
renderFinalCommentsDue,
|
||||
isQuestionnaire,
|
||||
whichProgress
|
||||
}) => {
|
||||
return (
|
||||
<div className="govuk-grid-column-one-quarter">
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<h2 className="govuk-heading-s">
|
||||
{t("myrepresentations:timetable-title")}
|
||||
</h2>
|
||||
<div className="govuk-summary-list">
|
||||
<div className="govuk-body-s">
|
||||
{appealIsLocalImpact
|
||||
? t("myrepresentations:local-impact-report")
|
||||
: t(
|
||||
"myrepresentations:questionnaire-due-label"
|
||||
)}
|
||||
:{" "}
|
||||
<span className="govuk-body-s">
|
||||
{renderDueDate()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{!appealIsConsultation && renderStatementsDue()}
|
||||
|
||||
{renderMarineImpact()}
|
||||
|
||||
{renderConsultationClose()}
|
||||
|
||||
{renderFinalCommentsDue()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isQuestionnaire && whichProgress()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RepresentationTimetablePanel;
|
||||
@@ -0,0 +1,93 @@
|
||||
import { JSONPath as jsonpath } from "jsonpath-plus";
|
||||
|
||||
export const resolveInitialCaseDataSources = ({
|
||||
hasState,
|
||||
isEditState,
|
||||
currentType,
|
||||
myRepresentations,
|
||||
searchDetailsObj,
|
||||
searchResultsObj,
|
||||
watchedCasesDetails
|
||||
}) => {
|
||||
if (hasState) {
|
||||
return {
|
||||
"caseDetailsSource": isEditState
|
||||
? myRepresentations
|
||||
: searchDetailsObj,
|
||||
"caseResultsSource": isEditState
|
||||
? myRepresentations
|
||||
: searchResultsObj
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
"caseDetailsSource":
|
||||
currentType === "watchedCases"
|
||||
? watchedCasesDetails
|
||||
: searchDetailsObj,
|
||||
"caseResultsSource":
|
||||
currentType === "watchedCases"
|
||||
? watchedCasesDetails
|
||||
: searchResultsObj
|
||||
};
|
||||
};
|
||||
|
||||
export const findResultsObjForCurrentReference = ({
|
||||
currentReference,
|
||||
caseResultsSource
|
||||
}) => {
|
||||
return jsonpath({
|
||||
path: `$..[?(@ && @.ticketnumber=="${currentReference}")]`,
|
||||
"json": caseResultsSource,
|
||||
"eval": true
|
||||
})[0];
|
||||
};
|
||||
|
||||
export const findDetailsObjForView = ({
|
||||
hasState,
|
||||
isEditState,
|
||||
queryCreated,
|
||||
ticketnumber,
|
||||
caseDetailsSource
|
||||
}) => {
|
||||
if (hasState && isEditState) {
|
||||
return jsonpath({
|
||||
path: `$..[?(@ && @.repfile_name=="${queryCreated}")]`,
|
||||
"json": caseDetailsSource,
|
||||
"eval": true
|
||||
})[0];
|
||||
}
|
||||
|
||||
return jsonpath({
|
||||
path: `$..[?(@ && @.ticketnumber=="${ticketnumber}")]`,
|
||||
"json": caseDetailsSource,
|
||||
"eval": true
|
||||
})[0];
|
||||
};
|
||||
|
||||
export const resolveSubmitCaseDetailsSource = ({
|
||||
isEditState,
|
||||
currentType,
|
||||
myRepresentations,
|
||||
watchedCasesDetails,
|
||||
searchDetailsObj
|
||||
}) => {
|
||||
if (isEditState) {
|
||||
return myRepresentations;
|
||||
}
|
||||
|
||||
return currentType === "watchedCases"
|
||||
? watchedCasesDetails
|
||||
: searchDetailsObj;
|
||||
};
|
||||
|
||||
export const findDetailsObjForSubmit = ({
|
||||
ticketnumber,
|
||||
caseDetailsSource
|
||||
}) => {
|
||||
return jsonpath({
|
||||
path: `$..[?(@ && @.ticketnumber=="${ticketnumber}")]`,
|
||||
"json": caseDetailsSource,
|
||||
"eval": true
|
||||
})[0];
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
export const buildRepresentationUpdateBody = ({
|
||||
formattedValues,
|
||||
setRepFileName
|
||||
}) => {
|
||||
let updateBody = setRepFileName(formattedValues);
|
||||
|
||||
const sanitizedBody = Object.entries(updateBody).reduce(
|
||||
(acc, [key, val]) => {
|
||||
if (val !== null && val !== undefined && !key.startsWith("_")) {
|
||||
acc[key] = val;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
const stringified = JSON.stringify(sanitizedBody)
|
||||
.replace(/:"Yes"/g, ":true")
|
||||
.replace(/:"No"/g, ":false");
|
||||
|
||||
updateBody = JSON.parse(stringified);
|
||||
|
||||
return updateBody;
|
||||
};
|
||||
|
||||
export const buildSubmitEnrichedValues = ({
|
||||
formValues,
|
||||
accountDetails,
|
||||
appealType,
|
||||
caseReferenceObj,
|
||||
detailsObj,
|
||||
resultsObj,
|
||||
representationCapacity,
|
||||
locale
|
||||
}) => {
|
||||
const buildSiteAddress = () => {
|
||||
const {
|
||||
pinswg_siteaddressline1,
|
||||
pinswg_siteaddressline2,
|
||||
pinswg_siteaddresstown,
|
||||
pinswg_siteaddresspostcode
|
||||
} = detailsObj;
|
||||
|
||||
return [
|
||||
pinswg_siteaddressline1,
|
||||
pinswg_siteaddressline2,
|
||||
pinswg_siteaddresstown,
|
||||
pinswg_siteaddresspostcode
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
};
|
||||
|
||||
return {
|
||||
...formValues,
|
||||
containerID: accountDetails.containerID,
|
||||
appealType: appealType || caseReferenceObj.repDetails.appealType,
|
||||
incidentID:
|
||||
caseReferenceObj.incidentid ||
|
||||
caseReferenceObj.repDetails.incidentid,
|
||||
caseRef: caseReferenceObj.currentReference,
|
||||
casereference: caseReferenceObj.currentReference,
|
||||
pinswg_questionnaireduedate: detailsObj.pinswg_questionnaireduedate,
|
||||
pinswg_endofrepresentationperiod:
|
||||
detailsObj.pinswg_endofrepresentationperiod,
|
||||
pinswg_applicationacceptedasvalid:
|
||||
detailsObj.pinswg_applicationacceptedasvalid,
|
||||
pinswg_statementduedate: detailsObj.pinswg_statementduedate,
|
||||
pinswg_statementsduedate: detailsObj.pinswg_statementsduedate,
|
||||
pinswg_finalcommentsduedate: detailsObj.pinswg_finalcommentsduedate,
|
||||
pinswg_name: caseReferenceObj.currentReference,
|
||||
firstname: accountDetails.firstname,
|
||||
lastname: accountDetails.lastname,
|
||||
emailAddress: accountDetails.emailaddress1,
|
||||
siteAddress: buildSiteAddress(),
|
||||
pinswg_siteaddressline1: detailsObj.pinswg_siteaddressline1,
|
||||
pinswg_siteaddressline2: detailsObj.pinswg_siteaddressline2,
|
||||
pinswg_siteaddresstown: detailsObj.pinswg_siteaddresstown,
|
||||
pinswg_siteaddresspostcode: detailsObj.pinswg_siteaddresspostcode,
|
||||
pinswg_lpareference: resultsObj?.pinswg_lpareference,
|
||||
_pinswg_appellant_value: detailsObj._pinswg_appellant_value,
|
||||
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue":
|
||||
detailsObj[
|
||||
"_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue"
|
||||
],
|
||||
_pinswg_localplanningauthority_value:
|
||||
detailsObj._pinswg_localplanningauthority_value ||
|
||||
detailsObj._pinswg_associatedlpa_value,
|
||||
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue":
|
||||
detailsObj[
|
||||
"_pinswg_localplanningauthority_value@OData.Community.Display.V1.FormattedValue"
|
||||
] ||
|
||||
detailsObj[
|
||||
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
|
||||
],
|
||||
representationCapacity,
|
||||
locale
|
||||
};
|
||||
};
|
||||
|
||||
export const runFinalisationSequence = ({
|
||||
values,
|
||||
containerID,
|
||||
currentReference,
|
||||
setFinaliseAppealProcess,
|
||||
generateRepPDF,
|
||||
updateLinks,
|
||||
uploadRepresentationFiles,
|
||||
setRepresentationSubmit,
|
||||
setRepresentationSubmitConfirmation
|
||||
}) => {
|
||||
setFinaliseAppealProcess(true);
|
||||
|
||||
return generateRepPDF(values, containerID, currentReference).then(
|
||||
(data) => {
|
||||
if (data.status === "success") {
|
||||
const updatedValues = updateLinks(values);
|
||||
uploadRepresentationFiles(updatedValues);
|
||||
setRepresentationSubmit(true);
|
||||
setRepresentationSubmitConfirmation(true);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,73 @@
|
||||
export const resolveJourneyStageFlags = ({
|
||||
representationSubmit,
|
||||
representationSubmitConfirmation
|
||||
}) => {
|
||||
const showCheckStage =
|
||||
representationSubmit === true || representationSubmit === "true";
|
||||
const showCompletionStage =
|
||||
representationSubmitConfirmation === true ||
|
||||
representationSubmitConfirmation === "true";
|
||||
|
||||
return {
|
||||
showFormStage: !showCheckStage,
|
||||
showCheckStage,
|
||||
showCompletionStage
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveRepresentationControlKey = ({
|
||||
isLPA,
|
||||
representationCapacity,
|
||||
repDetailsCapacity,
|
||||
appealType,
|
||||
normalizeCapacity
|
||||
}) => {
|
||||
if (isLPA && (!representationCapacity || representationCapacity === "")) {
|
||||
return {
|
||||
controlKey: "lpa-capacity-selection",
|
||||
normalizedCapacity: ""
|
||||
};
|
||||
}
|
||||
|
||||
const rawCapacity = representationCapacity || repDetailsCapacity || "false";
|
||||
const normalizedCapacity = normalizeCapacity(rawCapacity.toLowerCase());
|
||||
|
||||
return {
|
||||
controlKey: `capacity-${normalizedCapacity}`,
|
||||
normalizedCapacity
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveSubmitTransition = ({
|
||||
representationType,
|
||||
showQuestionnaireSection,
|
||||
questionnaireCount
|
||||
}) => {
|
||||
if (representationType !== "Questionnaire") {
|
||||
return "goToCheck";
|
||||
}
|
||||
|
||||
if (
|
||||
questionnaireCount !== 0 &&
|
||||
showQuestionnaireSection < questionnaireCount
|
||||
) {
|
||||
return "advanceQuestionnaire";
|
||||
}
|
||||
|
||||
return "goToCheck";
|
||||
};
|
||||
|
||||
export const getQuestionnaireNextSection = (
|
||||
currentSection,
|
||||
questionnaireCount
|
||||
) => {
|
||||
if (questionnaireCount !== 0 && currentSection < questionnaireCount) {
|
||||
return currentSection + 1;
|
||||
}
|
||||
|
||||
return currentSection;
|
||||
};
|
||||
|
||||
export const getQuestionnairePreviousSection = (currentSection) => {
|
||||
return currentSection > 1 ? currentSection - 1 : currentSection;
|
||||
};
|
||||
Reference in New Issue
Block a user